forked from Qortal/qortal
Compare commits
29 Commits
v2.1.2
...
mempow-tes
Author | SHA1 | Date | |
---|---|---|---|
|
5a1a814003 | ||
|
e59abe1b14 | ||
|
4f8721e629 | ||
|
59ffb65b8c | ||
|
cf603aa80e | ||
|
35a5dc6219 | ||
|
ace3ca0ad9 | ||
|
a8a498ddea | ||
|
d16663f0a9 | ||
|
9ce748452d | ||
|
9263d74b75 | ||
|
9601bddc84 | ||
|
e281e19052 | ||
|
0238b78f45 | ||
|
0ccee4326d | ||
|
391c3fe4c9 | ||
|
3a7da9f13b | ||
|
e7fd803d19 | ||
|
7c5c010eeb | ||
|
250d1315f0 | ||
|
6da671ba86 | ||
|
bf10c46d9f | ||
|
a2fbdbe295 | ||
|
b40e6cb933 | ||
|
03ca36c990 | ||
|
e3505836f3 | ||
|
6548e4c07d | ||
|
083c534e61 | ||
|
84a4b15019 |
@@ -17,10 +17,10 @@
|
||||
<ROW Property="Manufacturer" Value="Qortal"/>
|
||||
<ROW Property="MsiLogging" MultiBuildValue="DefaultBuild:vp"/>
|
||||
<ROW Property="NTP_GOOD" Value="false"/>
|
||||
<ROW Property="ProductCode" Value="1033:{817F888F-5FFB-40A4-B839-941BB9426D93} 1049:{402239D3-5D8E-4FFC-9BAC-1920DBCC6D34} 2052:{BB2EB961-8D52-43AD-9BDE-8B7CA4D5985B} 2057:{C1281311-FCA8-4B2B-AB06-7DD75CE0D302} " Type="16"/>
|
||||
<ROW Property="ProductCode" Value="1033:{51EFA0B0-C304-4043-AF6D-2C17C783A998} 1049:{C4662BB2-A247-426E-A128-B7DBD12ECE78} 2052:{1AF44520-C8AB-4261-BCFE-EEA941439718} 2057:{C096EB6A-F43F-45EE-921C-D20F9B993E80} " Type="16"/>
|
||||
<ROW Property="ProductLanguage" Value="2057"/>
|
||||
<ROW Property="ProductName" Value="Qortal"/>
|
||||
<ROW Property="ProductVersion" Value="2.1.1" Type="32"/>
|
||||
<ROW Property="ProductVersion" Value="2.1.3" Type="32"/>
|
||||
<ROW Property="RECONFIG_NTP" Value="true"/>
|
||||
<ROW Property="REMOVE_BLOCKCHAIN" Value="YES" Type="4"/>
|
||||
<ROW Property="REPAIR_BLOCKCHAIN" Value="YES" Type="4"/>
|
||||
@@ -212,7 +212,7 @@
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_71" ComponentId="{12A3ADBE-BB7A-496C-8869-410681E6232F}" Directory_="jdk.zipfs_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_71" Type="0"/>
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_8" ComponentId="{D53AD95E-CF96-4999-80FC-5812277A7456}" Directory_="java.naming_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_8" Type="0"/>
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_9" ComponentId="{6B7EA9B0-5D17-47A8-B78C-FACE86D15E01}" Directory_="java.net.http_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_9" Type="0"/>
|
||||
<ROW Component="AI_CustomARPName" ComponentId="{E9D8F0C9-6CF8-477D-9410-49FB5425AEB1}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
||||
<ROW Component="AI_CustomARPName" ComponentId="{9A243E53-8FC9-4854-B6E2-937493305BB4}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
||||
<ROW Component="AI_ExePath" ComponentId="{3644948D-AE0B-41BB-9FAF-A79E70490A08}" Directory_="APPDIR" Attributes="260" KeyPath="AI_ExePath"/>
|
||||
<ROW Component="APPDIR" ComponentId="{680DFDDE-3FB4-47A5-8FF5-934F576C6F91}" Directory_="APPDIR" Attributes="0"/>
|
||||
<ROW Component="AccessBridgeCallbacks.h" ComponentId="{288055D1-1062-47A3-AA44-5601B4E38AED}" Directory_="bridge_Dir" Attributes="0" KeyPath="AccessBridgeCallbacks.h" Type="0"/>
|
||||
|
2
pom.xml
2
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.qortal</groupId>
|
||||
<artifactId>qortal</artifactId>
|
||||
<version>2.1.2</version>
|
||||
<version>2.1.3-mempow.0</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<skipTests>true</skipTests>
|
||||
|
@@ -34,7 +34,7 @@ import org.qortal.api.Security;
|
||||
import org.qortal.api.model.ApiOnlineAccount;
|
||||
import org.qortal.api.model.RewardShareKeyRequest;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.controller.OnlineAccountsManager;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.account.RewardShareData;
|
||||
@@ -160,7 +160,7 @@ public class AddressesResource {
|
||||
)
|
||||
@ApiErrors({ApiError.PUBLIC_KEY_NOT_FOUND, ApiError.REPOSITORY_ISSUE})
|
||||
public List<ApiOnlineAccount> getOnlineAccounts() {
|
||||
List<OnlineAccountData> onlineAccounts = Controller.getInstance().getOnlineAccounts();
|
||||
List<OnlineAccountData> onlineAccounts = OnlineAccountsManager.getInstance().getOnlineAccounts();
|
||||
|
||||
// Map OnlineAccountData entries to OnlineAccount via reward-share data
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
@@ -195,7 +195,7 @@ public class AddressesResource {
|
||||
)
|
||||
@ApiErrors({ApiError.PUBLIC_KEY_NOT_FOUND, ApiError.REPOSITORY_ISSUE})
|
||||
public List<OnlineAccountLevel> getOnlineAccountsByLevel() {
|
||||
List<OnlineAccountData> onlineAccounts = Controller.getInstance().getOnlineAccounts();
|
||||
List<OnlineAccountData> onlineAccounts = OnlineAccountsManager.getInstance().getOnlineAccounts();
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<OnlineAccountLevel> onlineAccountLevels = new ArrayList<>();
|
||||
|
@@ -75,7 +75,8 @@ public class BlocksResource {
|
||||
@ApiErrors({
|
||||
ApiError.INVALID_SIGNATURE, ApiError.BLOCK_UNKNOWN, ApiError.REPOSITORY_ISSUE
|
||||
})
|
||||
public BlockData getBlock(@PathParam("signature") String signature58) {
|
||||
public BlockData getBlock(@PathParam("signature") String signature58,
|
||||
@QueryParam("includeOnlineSignatures") Boolean includeOnlineSignatures) {
|
||||
// Decode signature
|
||||
byte[] signature;
|
||||
try {
|
||||
@@ -88,12 +89,18 @@ public class BlocksResource {
|
||||
// Check the database first
|
||||
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
|
||||
if (blockData != null) {
|
||||
if (includeOnlineSignatures == null || includeOnlineSignatures == false) {
|
||||
blockData.setOnlineAccountsSignatures(null);
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
|
||||
// Not found, so try the block archive
|
||||
blockData = repository.getBlockArchiveRepository().fromSignature(signature);
|
||||
if (blockData != null) {
|
||||
if (includeOnlineSignatures == null || includeOnlineSignatures == false) {
|
||||
blockData.setOnlineAccountsSignatures(null);
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
|
||||
@@ -423,17 +430,24 @@ public class BlocksResource {
|
||||
@ApiErrors({
|
||||
ApiError.BLOCK_UNKNOWN, ApiError.REPOSITORY_ISSUE
|
||||
})
|
||||
public BlockData getByHeight(@PathParam("height") int height) {
|
||||
public BlockData getByHeight(@PathParam("height") int height,
|
||||
@QueryParam("includeOnlineSignatures") Boolean includeOnlineSignatures) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
// Firstly check the database
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(height);
|
||||
if (blockData != null) {
|
||||
if (includeOnlineSignatures == null || includeOnlineSignatures == false) {
|
||||
blockData.setOnlineAccountsSignatures(null);
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
|
||||
// Not found, so try the archive
|
||||
blockData = repository.getBlockArchiveRepository().fromHeight(height);
|
||||
if (blockData != null) {
|
||||
if (includeOnlineSignatures == null || includeOnlineSignatures == false) {
|
||||
blockData.setOnlineAccountsSignatures(null);
|
||||
}
|
||||
return blockData;
|
||||
}
|
||||
|
||||
|
@@ -28,7 +28,7 @@ import org.qortal.asset.Asset;
|
||||
import org.qortal.at.AT;
|
||||
import org.qortal.block.BlockChain.BlockTimingByHeight;
|
||||
import org.qortal.block.BlockChain.AccountLevelShareBin;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.controller.OnlineAccountsManager;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.account.AccountBalanceData;
|
||||
import org.qortal.data.account.AccountData;
|
||||
@@ -320,7 +320,7 @@ public class Block {
|
||||
byte[] reference = parentBlockData.getSignature();
|
||||
|
||||
// Fetch our list of online accounts
|
||||
List<OnlineAccountData> onlineAccounts = Controller.getInstance().getOnlineAccounts();
|
||||
List<OnlineAccountData> onlineAccounts = OnlineAccountsManager.getInstance().getOnlineAccounts();
|
||||
if (onlineAccounts.isEmpty()) {
|
||||
LOGGER.error("No online accounts - not even our own?");
|
||||
return null;
|
||||
@@ -976,10 +976,10 @@ public class Block {
|
||||
byte[] onlineTimestampBytes = Longs.toByteArray(onlineTimestamp);
|
||||
|
||||
// If this block is much older than current online timestamp, then there's no point checking current online accounts
|
||||
List<OnlineAccountData> currentOnlineAccounts = onlineTimestamp < NTP.getTime() - Controller.ONLINE_TIMESTAMP_MODULUS
|
||||
List<OnlineAccountData> currentOnlineAccounts = onlineTimestamp < NTP.getTime() - OnlineAccountsManager.ONLINE_TIMESTAMP_MODULUS
|
||||
? null
|
||||
: Controller.getInstance().getOnlineAccounts();
|
||||
List<OnlineAccountData> latestBlocksOnlineAccounts = Controller.getInstance().getLatestBlocksOnlineAccounts();
|
||||
: OnlineAccountsManager.getInstance().getOnlineAccounts();
|
||||
List<OnlineAccountData> latestBlocksOnlineAccounts = OnlineAccountsManager.getInstance().getLatestBlocksOnlineAccounts();
|
||||
|
||||
// Extract online accounts' timestamp signatures from block data
|
||||
List<byte[]> onlineAccountsSignatures = BlockTransformer.decodeTimestampSignatures(this.blockData.getOnlineAccountsSignatures());
|
||||
@@ -1357,7 +1357,7 @@ public class Block {
|
||||
postBlockTidy();
|
||||
|
||||
// Give Controller our cached, valid online accounts data (if any) to help reduce CPU load for next block
|
||||
Controller.getInstance().pushLatestBlocksOnlineAccounts(this.cachedValidOnlineAccounts);
|
||||
OnlineAccountsManager.getInstance().pushLatestBlocksOnlineAccounts(this.cachedValidOnlineAccounts);
|
||||
|
||||
// Log some debugging info relating to the block weight calculation
|
||||
this.logDebugInfo();
|
||||
@@ -1575,8 +1575,8 @@ public class Block {
|
||||
|
||||
postBlockTidy();
|
||||
|
||||
// Remove any cached, valid online accounts data from Controller
|
||||
Controller.getInstance().popLatestBlocksOnlineAccounts();
|
||||
// Remove any cached, valid online accounts data from OnlineAccountsManager
|
||||
OnlineAccountsManager.getInstance().popLatestBlocksOnlineAccounts();
|
||||
}
|
||||
|
||||
protected void orphanTransactionsFromBlock() throws DataException {
|
||||
|
@@ -109,7 +109,7 @@ public class BlockMinter extends Thread {
|
||||
continue;
|
||||
|
||||
// No online accounts? (e.g. during startup)
|
||||
if (Controller.getInstance().getOnlineAccounts().isEmpty())
|
||||
if (OnlineAccountsManager.getInstance().getOnlineAccounts().isEmpty())
|
||||
continue;
|
||||
|
||||
List<MintingAccountData> mintingAccountsData = repository.getAccountRepository().getMintingAccounts();
|
||||
@@ -429,7 +429,7 @@ public class BlockMinter extends Thread {
|
||||
throw new DataException("Ignoring attempt to mint testing block for non-test chain!");
|
||||
|
||||
// Ensure mintingAccount is 'online' so blocks can be minted
|
||||
Controller.getInstance().ensureTestingAccountsOnline(mintingAndOnlineAccounts);
|
||||
OnlineAccountsManager.getInstance().ensureTestingAccountsOnline(mintingAndOnlineAccounts);
|
||||
|
||||
PrivateKeyAccount mintingAccount = mintingAndOnlineAccounts[0];
|
||||
|
||||
|
@@ -12,7 +12,6 @@ import java.security.Security;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -41,9 +40,6 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.bouncycastle.jsse.provider.BouncyCastleJsseProvider;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.account.PublicKeyAccount;
|
||||
import org.qortal.api.ApiService;
|
||||
import org.qortal.block.Block;
|
||||
import org.qortal.block.BlockChain;
|
||||
@@ -53,11 +49,8 @@ import org.qortal.controller.repository.PruneManager;
|
||||
import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
|
||||
import org.qortal.controller.tradebot.TradeBot;
|
||||
import org.qortal.crypto.Crypto;
|
||||
import org.qortal.data.account.MintingAccountData;
|
||||
import org.qortal.data.account.RewardShareData;
|
||||
import org.qortal.data.block.BlockData;
|
||||
import org.qortal.data.block.BlockSummaryData;
|
||||
import org.qortal.data.network.OnlineAccountData;
|
||||
import org.qortal.data.network.PeerChainTipData;
|
||||
import org.qortal.data.network.PeerData;
|
||||
import org.qortal.data.transaction.ArbitraryTransactionData;
|
||||
@@ -77,14 +70,12 @@ import org.qortal.network.message.CachedBlockMessage;
|
||||
import org.qortal.network.message.GetArbitraryDataMessage;
|
||||
import org.qortal.network.message.GetBlockMessage;
|
||||
import org.qortal.network.message.GetBlockSummariesMessage;
|
||||
import org.qortal.network.message.GetOnlineAccountsMessage;
|
||||
import org.qortal.network.message.GetPeersMessage;
|
||||
import org.qortal.network.message.GetSignaturesV2Message;
|
||||
import org.qortal.network.message.GetTransactionMessage;
|
||||
import org.qortal.network.message.GetUnconfirmedTransactionsMessage;
|
||||
import org.qortal.network.message.HeightV2Message;
|
||||
import org.qortal.network.message.Message;
|
||||
import org.qortal.network.message.OnlineAccountsMessage;
|
||||
import org.qortal.network.message.SignaturesMessage;
|
||||
import org.qortal.network.message.TransactionMessage;
|
||||
import org.qortal.network.message.TransactionSignaturesMessage;
|
||||
@@ -97,7 +88,6 @@ import org.qortal.transaction.Transaction.TransactionType;
|
||||
import org.qortal.transaction.Transaction.ValidationResult;
|
||||
import org.qortal.utils.*;
|
||||
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
public class Controller extends Thread {
|
||||
|
||||
@@ -122,14 +112,6 @@ public class Controller extends Thread {
|
||||
private static final long DELETE_EXPIRED_INTERVAL = 5 * 60 * 1000L; // ms
|
||||
private static final long RECOVERY_MODE_TIMEOUT = 10 * 60 * 1000L; // ms
|
||||
|
||||
// To do with online accounts list
|
||||
private static final long ONLINE_ACCOUNTS_TASKS_INTERVAL = 10 * 1000L; // ms
|
||||
private static final long ONLINE_ACCOUNTS_BROADCAST_INTERVAL = 1 * 60 * 1000L; // ms
|
||||
public static final long ONLINE_TIMESTAMP_MODULUS = 5 * 60 * 1000L;
|
||||
private static final long LAST_SEEN_EXPIRY_PERIOD = (ONLINE_TIMESTAMP_MODULUS * 2) + (1 * 60 * 1000L);
|
||||
/** How many (latest) blocks' worth of online accounts we cache */
|
||||
private static final int MAX_BLOCKS_CACHED_ONLINE_ACCOUNTS = 2;
|
||||
|
||||
private static volatile boolean isStopping = false;
|
||||
private static BlockMinter blockMinter = null;
|
||||
private static volatile boolean requestSync = false;
|
||||
@@ -161,8 +143,6 @@ public class Controller extends Thread {
|
||||
private long ntpCheckTimestamp = startTime; // ms
|
||||
private long deleteExpiredTimestamp = startTime + DELETE_EXPIRED_INTERVAL; // ms
|
||||
|
||||
private long onlineAccountsTasksTimestamp = startTime + ONLINE_ACCOUNTS_TASKS_INTERVAL; // ms
|
||||
|
||||
/** Whether we can mint new blocks, as reported by BlockMinter. */
|
||||
private volatile boolean isMintingPossible = false;
|
||||
|
||||
@@ -203,11 +183,6 @@ public class Controller extends Thread {
|
||||
/** Lock for only allowing one blockchain-modifying codepath at a time. e.g. synchronization or newly minted block. */
|
||||
private final ReentrantLock blockchainLock = new ReentrantLock();
|
||||
|
||||
/** Cache of current 'online accounts' */
|
||||
List<OnlineAccountData> onlineAccounts = new ArrayList<>();
|
||||
/** Cache of latest blocks' online accounts */
|
||||
Deque<List<OnlineAccountData>> latestBlocksOnlineAccounts = new ArrayDeque<>(MAX_BLOCKS_CACHED_ONLINE_ACCOUNTS);
|
||||
|
||||
// Stats
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class StatsSnapshot {
|
||||
@@ -438,10 +413,12 @@ public class Controller extends Thread {
|
||||
return; // Not System.exit() so that GUI can display error
|
||||
}
|
||||
|
||||
// Rebuild Names table and check database integrity
|
||||
// Rebuild Names table and check database integrity (if enabled)
|
||||
NamesDatabaseIntegrityCheck namesDatabaseIntegrityCheck = new NamesDatabaseIntegrityCheck();
|
||||
namesDatabaseIntegrityCheck.rebuildAllNames();
|
||||
namesDatabaseIntegrityCheck.runIntegrityCheck();
|
||||
if (Settings.getInstance().isNamesIntegrityCheckEnabled()) {
|
||||
namesDatabaseIntegrityCheck.runIntegrityCheck();
|
||||
}
|
||||
|
||||
LOGGER.info("Validating blockchain");
|
||||
try {
|
||||
@@ -495,6 +472,10 @@ public class Controller extends Thread {
|
||||
// LOGGER.info("Starting arbitrary-transaction data manager");
|
||||
// ArbitraryDataManager.getInstance().start();
|
||||
|
||||
// Online accounts manager
|
||||
LOGGER.info("Starting online accounts manager");
|
||||
OnlineAccountsManager.getInstance().start();
|
||||
|
||||
// Auto-update service?
|
||||
if (Settings.getInstance().isAutoUpdateEnabled()) {
|
||||
LOGGER.info("Starting auto-update");
|
||||
@@ -639,11 +620,9 @@ public class Controller extends Thread {
|
||||
deleteExpiredTransactions();
|
||||
}
|
||||
|
||||
// Perform tasks to do with managing online accounts list
|
||||
if (now >= onlineAccountsTasksTimestamp) {
|
||||
onlineAccountsTasksTimestamp = now + ONLINE_ACCOUNTS_TASKS_INTERVAL;
|
||||
performOnlineAccountsTasks();
|
||||
}
|
||||
// Check and maybe perform online accounts tasks
|
||||
OnlineAccountsManager.getInstance().checkOnlineAccountsTasks(now);
|
||||
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
// Clear interrupted flag so we can shutdown trim threads
|
||||
@@ -1056,6 +1035,10 @@ public class Controller extends Thread {
|
||||
// LOGGER.info("Shutting down arbitrary-transaction data manager");
|
||||
// ArbitraryDataManager.getInstance().shutdown();
|
||||
|
||||
// Online accounts manager
|
||||
LOGGER.info("Shutting down online accounts manager");
|
||||
OnlineAccountsManager.getInstance().shutdown();
|
||||
|
||||
if (blockMinter != null) {
|
||||
LOGGER.info("Shutting down block minter");
|
||||
blockMinter.shutdown();
|
||||
@@ -1359,11 +1342,11 @@ public class Controller extends Thread {
|
||||
break;
|
||||
|
||||
case GET_ONLINE_ACCOUNTS:
|
||||
onNetworkGetOnlineAccountsMessage(peer, message);
|
||||
OnlineAccountsManager.getInstance().onNetworkGetOnlineAccountsMessage(peer, message);
|
||||
break;
|
||||
|
||||
case ONLINE_ACCOUNTS:
|
||||
onNetworkOnlineAccountsMessage(peer, message);
|
||||
OnlineAccountsManager.getInstance().onNetworkOnlineAccountsMessage(peer, message);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1828,299 +1811,6 @@ public class Controller extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
private void onNetworkGetOnlineAccountsMessage(Peer peer, Message message) {
|
||||
GetOnlineAccountsMessage getOnlineAccountsMessage = (GetOnlineAccountsMessage) message;
|
||||
|
||||
List<OnlineAccountData> excludeAccounts = getOnlineAccountsMessage.getOnlineAccounts();
|
||||
|
||||
// Send online accounts info, excluding entries with matching timestamp & public key from excludeAccounts
|
||||
List<OnlineAccountData> accountsToSend;
|
||||
synchronized (this.onlineAccounts) {
|
||||
accountsToSend = new ArrayList<>(this.onlineAccounts);
|
||||
}
|
||||
|
||||
Iterator<OnlineAccountData> iterator = accountsToSend.iterator();
|
||||
|
||||
SEND_ITERATOR:
|
||||
while (iterator.hasNext()) {
|
||||
OnlineAccountData onlineAccountData = iterator.next();
|
||||
|
||||
for (int i = 0; i < excludeAccounts.size(); ++i) {
|
||||
OnlineAccountData excludeAccountData = excludeAccounts.get(i);
|
||||
|
||||
if (onlineAccountData.getTimestamp() == excludeAccountData.getTimestamp() && Arrays.equals(onlineAccountData.getPublicKey(), excludeAccountData.getPublicKey())) {
|
||||
iterator.remove();
|
||||
continue SEND_ITERATOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message onlineAccountsMessage = new OnlineAccountsMessage(accountsToSend);
|
||||
peer.sendMessage(onlineAccountsMessage);
|
||||
|
||||
LOGGER.trace(() -> String.format("Sent %d of our %d online accounts to %s", accountsToSend.size(), this.onlineAccounts.size(), peer));
|
||||
}
|
||||
|
||||
private void onNetworkOnlineAccountsMessage(Peer peer, Message message) {
|
||||
OnlineAccountsMessage onlineAccountsMessage = (OnlineAccountsMessage) message;
|
||||
|
||||
List<OnlineAccountData> peersOnlineAccounts = onlineAccountsMessage.getOnlineAccounts();
|
||||
LOGGER.trace(() -> String.format("Received %d online accounts from %s", peersOnlineAccounts.size(), peer));
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
for (OnlineAccountData onlineAccountData : peersOnlineAccounts)
|
||||
this.verifyAndAddAccount(repository, onlineAccountData);
|
||||
} catch (DataException e) {
|
||||
LOGGER.error(String.format("Repository issue while verifying online accounts from peer %s", peer), e);
|
||||
}
|
||||
}
|
||||
|
||||
// Utilities
|
||||
|
||||
private void verifyAndAddAccount(Repository repository, OnlineAccountData onlineAccountData) throws DataException {
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
PublicKeyAccount otherAccount = new PublicKeyAccount(repository, onlineAccountData.getPublicKey());
|
||||
|
||||
// Check timestamp is 'recent' here
|
||||
if (Math.abs(onlineAccountData.getTimestamp() - now) > ONLINE_TIMESTAMP_MODULUS * 2) {
|
||||
LOGGER.trace(() -> String.format("Rejecting online account %s with out of range timestamp %d", otherAccount.getAddress(), onlineAccountData.getTimestamp()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify
|
||||
byte[] data = Longs.toByteArray(onlineAccountData.getTimestamp());
|
||||
if (!otherAccount.verify(onlineAccountData.getSignature(), data)) {
|
||||
LOGGER.trace(() -> String.format("Rejecting invalid online account %s", otherAccount.getAddress()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Qortal: check online account is actually reward-share
|
||||
RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(onlineAccountData.getPublicKey());
|
||||
if (rewardShareData == null) {
|
||||
// Reward-share doesn't even exist - probably not a good sign
|
||||
LOGGER.trace(() -> String.format("Rejecting unknown online reward-share public key %s", Base58.encode(onlineAccountData.getPublicKey())));
|
||||
return;
|
||||
}
|
||||
|
||||
Account mintingAccount = new Account(repository, rewardShareData.getMinter());
|
||||
if (!mintingAccount.canMint()) {
|
||||
// Minting-account component of reward-share can no longer mint - disregard
|
||||
LOGGER.trace(() -> String.format("Rejecting online reward-share with non-minting account %s", mintingAccount.getAddress()));
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this.onlineAccounts) {
|
||||
OnlineAccountData existingAccountData = this.onlineAccounts.stream().filter(account -> Arrays.equals(account.getPublicKey(), onlineAccountData.getPublicKey())).findFirst().orElse(null);
|
||||
|
||||
if (existingAccountData != null) {
|
||||
if (existingAccountData.getTimestamp() < onlineAccountData.getTimestamp()) {
|
||||
this.onlineAccounts.remove(existingAccountData);
|
||||
|
||||
LOGGER.trace(() -> String.format("Updated online account %s with timestamp %d (was %d)", otherAccount.getAddress(), onlineAccountData.getTimestamp(), existingAccountData.getTimestamp()));
|
||||
} else {
|
||||
LOGGER.trace(() -> String.format("Not updating existing online account %s", otherAccount.getAddress()));
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOGGER.trace(() -> String.format("Added online account %s with timestamp %d", otherAccount.getAddress(), onlineAccountData.getTimestamp()));
|
||||
}
|
||||
|
||||
this.onlineAccounts.add(onlineAccountData);
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureTestingAccountsOnline(PrivateKeyAccount... onlineAccounts) {
|
||||
if (!BlockChain.getInstance().isTestChain()) {
|
||||
LOGGER.warn("Ignoring attempt to ensure test account is online for non-test chain!");
|
||||
return;
|
||||
}
|
||||
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
final long onlineAccountsTimestamp = Controller.toOnlineAccountTimestamp(now);
|
||||
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
|
||||
|
||||
synchronized (this.onlineAccounts) {
|
||||
this.onlineAccounts.clear();
|
||||
|
||||
for (PrivateKeyAccount onlineAccount : onlineAccounts) {
|
||||
// Check mintingAccount is actually reward-share?
|
||||
|
||||
byte[] signature = onlineAccount.sign(timestampBytes);
|
||||
byte[] publicKey = onlineAccount.getPublicKey();
|
||||
|
||||
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
||||
this.onlineAccounts.add(ourOnlineAccountData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void performOnlineAccountsTasks() {
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
// Expire old entries
|
||||
final long cutoffThreshold = now - LAST_SEEN_EXPIRY_PERIOD;
|
||||
synchronized (this.onlineAccounts) {
|
||||
Iterator<OnlineAccountData> iterator = this.onlineAccounts.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
OnlineAccountData onlineAccountData = iterator.next();
|
||||
|
||||
if (onlineAccountData.getTimestamp() < cutoffThreshold) {
|
||||
iterator.remove();
|
||||
|
||||
LOGGER.trace(() -> {
|
||||
PublicKeyAccount otherAccount = new PublicKeyAccount(null, onlineAccountData.getPublicKey());
|
||||
return String.format("Removed expired online account %s with timestamp %d", otherAccount.getAddress(), onlineAccountData.getTimestamp());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request data from other peers?
|
||||
if ((this.onlineAccountsTasksTimestamp % ONLINE_ACCOUNTS_BROADCAST_INTERVAL) < ONLINE_ACCOUNTS_TASKS_INTERVAL) {
|
||||
Message message;
|
||||
synchronized (this.onlineAccounts) {
|
||||
message = new GetOnlineAccountsMessage(this.onlineAccounts);
|
||||
}
|
||||
Network.getInstance().broadcast(peer -> message);
|
||||
}
|
||||
|
||||
// Refresh our online accounts signatures?
|
||||
sendOurOnlineAccountsInfo();
|
||||
}
|
||||
|
||||
private void sendOurOnlineAccountsInfo() {
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
List<MintingAccountData> mintingAccounts;
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
mintingAccounts = repository.getAccountRepository().getMintingAccounts();
|
||||
|
||||
// We have no accounts, but don't reset timestamp
|
||||
if (mintingAccounts.isEmpty())
|
||||
return;
|
||||
|
||||
// Only reward-share accounts allowed
|
||||
Iterator<MintingAccountData> iterator = mintingAccounts.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MintingAccountData mintingAccountData = iterator.next();
|
||||
|
||||
RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(mintingAccountData.getPublicKey());
|
||||
if (rewardShareData == null) {
|
||||
// Reward-share doesn't even exist - probably not a good sign
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
Account mintingAccount = new Account(repository, rewardShareData.getMinter());
|
||||
if (!mintingAccount.canMint()) {
|
||||
// Minting-account component of reward-share can no longer mint - disregard
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (DataException e) {
|
||||
LOGGER.warn(String.format("Repository issue trying to fetch minting accounts: %s", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
// 'current' timestamp
|
||||
final long onlineAccountsTimestamp = Controller.toOnlineAccountTimestamp(now);
|
||||
boolean hasInfoChanged = false;
|
||||
|
||||
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
|
||||
List<OnlineAccountData> ourOnlineAccounts = new ArrayList<>();
|
||||
|
||||
MINTING_ACCOUNTS:
|
||||
for (MintingAccountData mintingAccountData : mintingAccounts) {
|
||||
PrivateKeyAccount mintingAccount = new PrivateKeyAccount(null, mintingAccountData.getPrivateKey());
|
||||
|
||||
byte[] signature = mintingAccount.sign(timestampBytes);
|
||||
byte[] publicKey = mintingAccount.getPublicKey();
|
||||
|
||||
// Our account is online
|
||||
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
||||
synchronized (this.onlineAccounts) {
|
||||
Iterator<OnlineAccountData> iterator = this.onlineAccounts.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
OnlineAccountData existingOnlineAccountData = iterator.next();
|
||||
|
||||
if (Arrays.equals(existingOnlineAccountData.getPublicKey(), ourOnlineAccountData.getPublicKey())) {
|
||||
// If our online account is already present, with same timestamp, then move on to next mintingAccount
|
||||
if (existingOnlineAccountData.getTimestamp() == onlineAccountsTimestamp)
|
||||
continue MINTING_ACCOUNTS;
|
||||
|
||||
// If our online account is already present, but with older timestamp, then remove it
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.onlineAccounts.add(ourOnlineAccountData);
|
||||
}
|
||||
|
||||
LOGGER.trace(() -> String.format("Added our online account %s with timestamp %d", mintingAccount.getAddress(), onlineAccountsTimestamp));
|
||||
ourOnlineAccounts.add(ourOnlineAccountData);
|
||||
hasInfoChanged = true;
|
||||
}
|
||||
|
||||
if (!hasInfoChanged)
|
||||
return;
|
||||
|
||||
Message message = new OnlineAccountsMessage(ourOnlineAccounts);
|
||||
Network.getInstance().broadcast(peer -> message);
|
||||
|
||||
LOGGER.trace(()-> String.format("Broadcasted %d online account%s with timestamp %d", ourOnlineAccounts.size(), (ourOnlineAccounts.size() != 1 ? "s" : ""), onlineAccountsTimestamp));
|
||||
}
|
||||
|
||||
public static long toOnlineAccountTimestamp(long timestamp) {
|
||||
return (timestamp / ONLINE_TIMESTAMP_MODULUS) * ONLINE_TIMESTAMP_MODULUS;
|
||||
}
|
||||
|
||||
/** Returns list of online accounts with timestamp recent enough to be considered currently online. */
|
||||
public List<OnlineAccountData> getOnlineAccounts() {
|
||||
final long onlineTimestamp = Controller.toOnlineAccountTimestamp(NTP.getTime());
|
||||
|
||||
synchronized (this.onlineAccounts) {
|
||||
return this.onlineAccounts.stream().filter(account -> account.getTimestamp() == onlineTimestamp).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns cached, unmodifiable list of latest block's online accounts. */
|
||||
public List<OnlineAccountData> getLatestBlocksOnlineAccounts() {
|
||||
synchronized (this.latestBlocksOnlineAccounts) {
|
||||
return this.latestBlocksOnlineAccounts.peekFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/** Caches list of latest block's online accounts. Typically called by Block.process() */
|
||||
public void pushLatestBlocksOnlineAccounts(List<OnlineAccountData> latestBlocksOnlineAccounts) {
|
||||
synchronized (this.latestBlocksOnlineAccounts) {
|
||||
if (this.latestBlocksOnlineAccounts.size() == MAX_BLOCKS_CACHED_ONLINE_ACCOUNTS)
|
||||
this.latestBlocksOnlineAccounts.pollLast();
|
||||
|
||||
this.latestBlocksOnlineAccounts.addFirst(latestBlocksOnlineAccounts == null
|
||||
? Collections.emptyList()
|
||||
: Collections.unmodifiableList(latestBlocksOnlineAccounts));
|
||||
}
|
||||
}
|
||||
|
||||
/** Reverts list of latest block's online accounts. Typically called by Block.orphan() */
|
||||
public void popLatestBlocksOnlineAccounts() {
|
||||
synchronized (this.latestBlocksOnlineAccounts) {
|
||||
this.latestBlocksOnlineAccounts.pollFirst();
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] fetchArbitraryData(byte[] signature) throws InterruptedException {
|
||||
// Build request
|
||||
|
404
src/main/java/org/qortal/controller/OnlineAccountsManager.java
Normal file
404
src/main/java/org/qortal/controller/OnlineAccountsManager.java
Normal file
@@ -0,0 +1,404 @@
|
||||
package org.qortal.controller;
|
||||
|
||||
import com.google.common.primitives.Longs;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.account.PublicKeyAccount;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.crypto.MemoryPoW;
|
||||
import org.qortal.data.account.MintingAccountData;
|
||||
import org.qortal.data.account.RewardShareData;
|
||||
import org.qortal.data.network.OnlineAccountData;
|
||||
import org.qortal.network.Network;
|
||||
import org.qortal.network.Peer;
|
||||
import org.qortal.network.message.GetOnlineAccountsMessage;
|
||||
import org.qortal.network.message.Message;
|
||||
import org.qortal.network.message.OnlineAccountsMessage;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.utils.Base58;
|
||||
import org.qortal.utils.NTP;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class OnlineAccountsManager extends Thread {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(OnlineAccountsManager.class);
|
||||
|
||||
private static OnlineAccountsManager instance;
|
||||
|
||||
private volatile boolean isStopping = false;
|
||||
|
||||
// To do with online accounts list
|
||||
private static final long ONLINE_ACCOUNTS_TASKS_INTERVAL = 10 * 1000L; // ms
|
||||
private static final long ONLINE_ACCOUNTS_BROADCAST_INTERVAL = 1 * 60 * 1000L; // ms
|
||||
public static final long ONLINE_TIMESTAMP_MODULUS = 5 * 60 * 1000L;
|
||||
private static final long LAST_SEEN_EXPIRY_PERIOD = (ONLINE_TIMESTAMP_MODULUS * 2) + (1 * 60 * 1000L);
|
||||
/** How many (latest) blocks' worth of online accounts we cache */
|
||||
private static final int MAX_BLOCKS_CACHED_ONLINE_ACCOUNTS = 2;
|
||||
|
||||
public static final int POW_BUFFER_SIZE = 8 * 1024 * 1024; // bytes
|
||||
|
||||
private long onlineAccountsTasksTimestamp = Controller.startTime + ONLINE_ACCOUNTS_TASKS_INTERVAL; // ms
|
||||
|
||||
/** Cache of current 'online accounts' */
|
||||
List<OnlineAccountData> onlineAccounts = new ArrayList<>();
|
||||
/** Cache of latest blocks' online accounts */
|
||||
Deque<List<OnlineAccountData>> latestBlocksOnlineAccounts = new ArrayDeque<>(MAX_BLOCKS_CACHED_ONLINE_ACCOUNTS);
|
||||
|
||||
public OnlineAccountsManager() {
|
||||
|
||||
}
|
||||
|
||||
public static synchronized OnlineAccountsManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new OnlineAccountsManager();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Thread.currentThread().setName("Online Accounts Manager");
|
||||
|
||||
try {
|
||||
while (!isStopping) {
|
||||
Thread.sleep(2000);
|
||||
|
||||
int difficulty = 15;
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
LOGGER.info("Computing nonce at difficulty {} for timestamp {}...", difficulty, timestamp);
|
||||
|
||||
byte[] bytes = Longs.toByteArray(timestamp);
|
||||
Integer nonce = MemoryPoW.compute2(bytes, POW_BUFFER_SIZE, difficulty);
|
||||
long totalTime = System.currentTimeMillis() - timestamp;
|
||||
double hashRate = nonce / (double)totalTime * 1000.0f;
|
||||
|
||||
LOGGER.info("Computed nonce: {}. Time taken: {} ms. Hashrate: {}", nonce, totalTime, hashRate);
|
||||
}
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
// Fall-through to exit thread...
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
isStopping = true;
|
||||
this.interrupt();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void checkOnlineAccountsTasks(long now) {
|
||||
// Perform tasks to do with managing online accounts list
|
||||
if (now >= onlineAccountsTasksTimestamp) {
|
||||
onlineAccountsTasksTimestamp = now + ONLINE_ACCOUNTS_TASKS_INTERVAL;
|
||||
performOnlineAccountsTasks();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendOurOnlineAccountsInfo() {
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
List<MintingAccountData> mintingAccounts;
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
mintingAccounts = repository.getAccountRepository().getMintingAccounts();
|
||||
|
||||
// We have no accounts, but don't reset timestamp
|
||||
if (mintingAccounts.isEmpty())
|
||||
return;
|
||||
|
||||
// Only reward-share accounts allowed
|
||||
Iterator<MintingAccountData> iterator = mintingAccounts.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
MintingAccountData mintingAccountData = iterator.next();
|
||||
|
||||
RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(mintingAccountData.getPublicKey());
|
||||
if (rewardShareData == null) {
|
||||
// Reward-share doesn't even exist - probably not a good sign
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
|
||||
Account mintingAccount = new Account(repository, rewardShareData.getMinter());
|
||||
if (!mintingAccount.canMint()) {
|
||||
// Minting-account component of reward-share can no longer mint - disregard
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
} catch (DataException e) {
|
||||
LOGGER.warn(String.format("Repository issue trying to fetch minting accounts: %s", e.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
// 'current' timestamp
|
||||
final long onlineAccountsTimestamp = OnlineAccountsManager.toOnlineAccountTimestamp(now);
|
||||
boolean hasInfoChanged = false;
|
||||
|
||||
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
|
||||
List<OnlineAccountData> ourOnlineAccounts = new ArrayList<>();
|
||||
|
||||
MINTING_ACCOUNTS:
|
||||
for (MintingAccountData mintingAccountData : mintingAccounts) {
|
||||
PrivateKeyAccount mintingAccount = new PrivateKeyAccount(null, mintingAccountData.getPrivateKey());
|
||||
|
||||
byte[] signature = mintingAccount.sign(timestampBytes);
|
||||
byte[] publicKey = mintingAccount.getPublicKey();
|
||||
|
||||
// Our account is online
|
||||
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
||||
synchronized (this.onlineAccounts) {
|
||||
Iterator<OnlineAccountData> iterator = this.onlineAccounts.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
OnlineAccountData existingOnlineAccountData = iterator.next();
|
||||
|
||||
if (Arrays.equals(existingOnlineAccountData.getPublicKey(), ourOnlineAccountData.getPublicKey())) {
|
||||
// If our online account is already present, with same timestamp, then move on to next mintingAccount
|
||||
if (existingOnlineAccountData.getTimestamp() == onlineAccountsTimestamp)
|
||||
continue MINTING_ACCOUNTS;
|
||||
|
||||
// If our online account is already present, but with older timestamp, then remove it
|
||||
iterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.onlineAccounts.add(ourOnlineAccountData);
|
||||
}
|
||||
|
||||
LOGGER.debug(() -> String.format("Added our online account %s with timestamp %d", mintingAccount.getAddress(), onlineAccountsTimestamp));
|
||||
ourOnlineAccounts.add(ourOnlineAccountData);
|
||||
hasInfoChanged = true;
|
||||
}
|
||||
|
||||
if (!hasInfoChanged)
|
||||
return;
|
||||
|
||||
Message message = new OnlineAccountsMessage(ourOnlineAccounts);
|
||||
Network.getInstance().broadcast(peer -> message);
|
||||
|
||||
LOGGER.debug(()-> String.format("Broadcasted %d online account%s with timestamp %d", ourOnlineAccounts.size(), (ourOnlineAccounts.size() != 1 ? "s" : ""), onlineAccountsTimestamp));
|
||||
}
|
||||
|
||||
private void performOnlineAccountsTasks() {
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
// Expire old entries
|
||||
final long cutoffThreshold = now - LAST_SEEN_EXPIRY_PERIOD;
|
||||
synchronized (this.onlineAccounts) {
|
||||
Iterator<OnlineAccountData> iterator = this.onlineAccounts.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
OnlineAccountData onlineAccountData = iterator.next();
|
||||
|
||||
if (onlineAccountData.getTimestamp() < cutoffThreshold) {
|
||||
iterator.remove();
|
||||
|
||||
LOGGER.trace(() -> {
|
||||
PublicKeyAccount otherAccount = new PublicKeyAccount(null, onlineAccountData.getPublicKey());
|
||||
return String.format("Removed expired online account %s with timestamp %d", otherAccount.getAddress(), onlineAccountData.getTimestamp());
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request data from other peers?
|
||||
if ((this.onlineAccountsTasksTimestamp % ONLINE_ACCOUNTS_BROADCAST_INTERVAL) < ONLINE_ACCOUNTS_TASKS_INTERVAL) {
|
||||
Message message;
|
||||
synchronized (this.onlineAccounts) {
|
||||
message = new GetOnlineAccountsMessage(this.onlineAccounts);
|
||||
}
|
||||
Network.getInstance().broadcast(peer -> message);
|
||||
}
|
||||
|
||||
// Refresh our online accounts signatures?
|
||||
sendOurOnlineAccountsInfo();
|
||||
}
|
||||
|
||||
public static long toOnlineAccountTimestamp(long timestamp) {
|
||||
return (timestamp / ONLINE_TIMESTAMP_MODULUS) * ONLINE_TIMESTAMP_MODULUS;
|
||||
}
|
||||
|
||||
/** Returns list of online accounts with timestamp recent enough to be considered currently online. */
|
||||
public List<OnlineAccountData> getOnlineAccounts() {
|
||||
final long onlineTimestamp = OnlineAccountsManager.toOnlineAccountTimestamp(NTP.getTime());
|
||||
|
||||
synchronized (this.onlineAccounts) {
|
||||
return this.onlineAccounts.stream().filter(account -> account.getTimestamp() == onlineTimestamp).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns cached, unmodifiable list of latest block's online accounts. */
|
||||
public List<OnlineAccountData> getLatestBlocksOnlineAccounts() {
|
||||
synchronized (this.latestBlocksOnlineAccounts) {
|
||||
return this.latestBlocksOnlineAccounts.peekFirst();
|
||||
}
|
||||
}
|
||||
|
||||
/** Caches list of latest block's online accounts. Typically called by Block.process() */
|
||||
public void pushLatestBlocksOnlineAccounts(List<OnlineAccountData> latestBlocksOnlineAccounts) {
|
||||
synchronized (this.latestBlocksOnlineAccounts) {
|
||||
if (this.latestBlocksOnlineAccounts.size() == MAX_BLOCKS_CACHED_ONLINE_ACCOUNTS)
|
||||
this.latestBlocksOnlineAccounts.pollLast();
|
||||
|
||||
this.latestBlocksOnlineAccounts.addFirst(latestBlocksOnlineAccounts == null
|
||||
? Collections.emptyList()
|
||||
: Collections.unmodifiableList(latestBlocksOnlineAccounts));
|
||||
}
|
||||
}
|
||||
|
||||
/** Reverts list of latest block's online accounts. Typically called by Block.orphan() */
|
||||
public void popLatestBlocksOnlineAccounts() {
|
||||
synchronized (this.latestBlocksOnlineAccounts) {
|
||||
this.latestBlocksOnlineAccounts.pollFirst();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Utilities
|
||||
|
||||
private void verifyAndAddAccount(Repository repository, OnlineAccountData onlineAccountData) throws DataException {
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
PublicKeyAccount otherAccount = new PublicKeyAccount(repository, onlineAccountData.getPublicKey());
|
||||
|
||||
// Check timestamp is 'recent' here
|
||||
if (Math.abs(onlineAccountData.getTimestamp() - now) > ONLINE_TIMESTAMP_MODULUS * 2) {
|
||||
LOGGER.trace(() -> String.format("Rejecting online account %s with out of range timestamp %d", otherAccount.getAddress(), onlineAccountData.getTimestamp()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify
|
||||
byte[] data = Longs.toByteArray(onlineAccountData.getTimestamp());
|
||||
if (!otherAccount.verify(onlineAccountData.getSignature(), data)) {
|
||||
LOGGER.trace(() -> String.format("Rejecting invalid online account %s", otherAccount.getAddress()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Qortal: check online account is actually reward-share
|
||||
RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(onlineAccountData.getPublicKey());
|
||||
if (rewardShareData == null) {
|
||||
// Reward-share doesn't even exist - probably not a good sign
|
||||
LOGGER.trace(() -> String.format("Rejecting unknown online reward-share public key %s", Base58.encode(onlineAccountData.getPublicKey())));
|
||||
return;
|
||||
}
|
||||
|
||||
Account mintingAccount = new Account(repository, rewardShareData.getMinter());
|
||||
if (!mintingAccount.canMint()) {
|
||||
// Minting-account component of reward-share can no longer mint - disregard
|
||||
LOGGER.trace(() -> String.format("Rejecting online reward-share with non-minting account %s", mintingAccount.getAddress()));
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this.onlineAccounts) {
|
||||
OnlineAccountData existingAccountData = this.onlineAccounts.stream().filter(account -> Arrays.equals(account.getPublicKey(), onlineAccountData.getPublicKey())).findFirst().orElse(null);
|
||||
|
||||
if (existingAccountData != null) {
|
||||
if (existingAccountData.getTimestamp() < onlineAccountData.getTimestamp()) {
|
||||
this.onlineAccounts.remove(existingAccountData);
|
||||
|
||||
LOGGER.trace(() -> String.format("Updated online account %s with timestamp %d (was %d)", otherAccount.getAddress(), onlineAccountData.getTimestamp(), existingAccountData.getTimestamp()));
|
||||
} else {
|
||||
LOGGER.trace(() -> String.format("Not updating existing online account %s", otherAccount.getAddress()));
|
||||
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
LOGGER.debug(() -> String.format("Added online account %s with timestamp %d", otherAccount.getAddress(), onlineAccountData.getTimestamp()));
|
||||
}
|
||||
|
||||
this.onlineAccounts.add(onlineAccountData);
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureTestingAccountsOnline(PrivateKeyAccount... onlineAccounts) {
|
||||
if (!BlockChain.getInstance().isTestChain()) {
|
||||
LOGGER.warn("Ignoring attempt to ensure test account is online for non-test chain!");
|
||||
return;
|
||||
}
|
||||
|
||||
final Long now = NTP.getTime();
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
final long onlineAccountsTimestamp = this.toOnlineAccountTimestamp(now);
|
||||
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
|
||||
|
||||
synchronized (this.onlineAccounts) {
|
||||
this.onlineAccounts.clear();
|
||||
|
||||
for (PrivateKeyAccount onlineAccount : onlineAccounts) {
|
||||
// Check mintingAccount is actually reward-share?
|
||||
|
||||
byte[] signature = onlineAccount.sign(timestampBytes);
|
||||
byte[] publicKey = onlineAccount.getPublicKey();
|
||||
|
||||
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
||||
this.onlineAccounts.add(ourOnlineAccountData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Network handlers
|
||||
|
||||
public void onNetworkGetOnlineAccountsMessage(Peer peer, Message message) {
|
||||
GetOnlineAccountsMessage getOnlineAccountsMessage = (GetOnlineAccountsMessage) message;
|
||||
|
||||
List<OnlineAccountData> excludeAccounts = getOnlineAccountsMessage.getOnlineAccounts();
|
||||
|
||||
// Send online accounts info, excluding entries with matching timestamp & public key from excludeAccounts
|
||||
List<OnlineAccountData> accountsToSend;
|
||||
synchronized (this.onlineAccounts) {
|
||||
accountsToSend = new ArrayList<>(this.onlineAccounts);
|
||||
}
|
||||
|
||||
Iterator<OnlineAccountData> iterator = accountsToSend.iterator();
|
||||
|
||||
SEND_ITERATOR:
|
||||
while (iterator.hasNext()) {
|
||||
OnlineAccountData onlineAccountData = iterator.next();
|
||||
|
||||
for (int i = 0; i < excludeAccounts.size(); ++i) {
|
||||
OnlineAccountData excludeAccountData = excludeAccounts.get(i);
|
||||
|
||||
if (onlineAccountData.getTimestamp() == excludeAccountData.getTimestamp() && Arrays.equals(onlineAccountData.getPublicKey(), excludeAccountData.getPublicKey())) {
|
||||
iterator.remove();
|
||||
continue SEND_ITERATOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Message onlineAccountsMessage = new OnlineAccountsMessage(accountsToSend);
|
||||
peer.sendMessage(onlineAccountsMessage);
|
||||
|
||||
LOGGER.debug(() -> String.format("Sent %d of our %d online accounts to %s", accountsToSend.size(), this.onlineAccounts.size(), peer));
|
||||
}
|
||||
|
||||
public void onNetworkOnlineAccountsMessage(Peer peer, Message message) {
|
||||
OnlineAccountsMessage onlineAccountsMessage = (OnlineAccountsMessage) message;
|
||||
|
||||
List<OnlineAccountData> peersOnlineAccounts = onlineAccountsMessage.getOnlineAccounts();
|
||||
LOGGER.debug(() -> String.format("Received %d online accounts from %s", peersOnlineAccounts.size(), peer));
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
for (OnlineAccountData onlineAccountData : peersOnlineAccounts)
|
||||
this.verifyAndAddAccount(repository, onlineAccountData);
|
||||
} catch (DataException e) {
|
||||
LOGGER.error(String.format("Repository issue while verifying online accounts from peer %s", peer), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -187,7 +187,12 @@ public class NamesDatabaseIntegrityCheck {
|
||||
// The old name will then be unregistered, or re-registered.
|
||||
// FUTURE: check database integrity for names that have been updated and then the original name re-registered
|
||||
else if (Objects.equals(updateNameTransactionData.getName(), registeredName)) {
|
||||
NameData newNameData = repository.getNameRepository().fromName(updateNameTransactionData.getNewName());
|
||||
String newName = updateNameTransactionData.getNewName();
|
||||
if (newName == null || newName.length() == 0) {
|
||||
// If new name is blank (or maybe null, just to be safe), it means that it stayed the same
|
||||
newName = registeredName;
|
||||
}
|
||||
NameData newNameData = repository.getNameRepository().fromName(newName);
|
||||
if (!Objects.equals(creator.getAddress(), newNameData.getOwner())) {
|
||||
LOGGER.info("Error: registered name {} is owned by {}, but it should be {}",
|
||||
updateNameTransactionData.getNewName(), newNameData.getOwner(), creator.getAddress());
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package org.qortal.crypto;
|
||||
|
||||
import org.qortal.controller.Controller;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class MemoryPoW {
|
||||
@@ -33,6 +35,10 @@ public class MemoryPoW {
|
||||
if (Thread.currentThread().isInterrupted())
|
||||
return -1;
|
||||
|
||||
// Or, if the Controller is stopping, do the same
|
||||
if (Controller.isStopping())
|
||||
return -1;
|
||||
|
||||
seed *= seedMultiplier; // per nonce
|
||||
|
||||
state[0] = longHash[0] ^ seed;
|
||||
|
@@ -204,6 +204,10 @@ public class BlockData implements Serializable {
|
||||
return this.onlineAccountsSignatures;
|
||||
}
|
||||
|
||||
public void setOnlineAccountsSignatures(byte[] onlineAccountsSignatures) {
|
||||
this.onlineAccountsSignatures = onlineAccountsSignatures;
|
||||
}
|
||||
|
||||
// JAXB special
|
||||
|
||||
@XmlElement(name = "minterAddress")
|
||||
|
@@ -147,13 +147,13 @@ public class SysTray {
|
||||
}
|
||||
});
|
||||
|
||||
JMenuItem openUi = new JMenuItem(Translator.INSTANCE.translate("SysTray", "OPEN_UI"));
|
||||
/* JMenuItem openUi = new JMenuItem(Translator.INSTANCE.translate("SysTray", "OPEN_UI"));
|
||||
openUi.addActionListener(actionEvent -> {
|
||||
destroyHiddenDialog();
|
||||
|
||||
new OpenUiWorker().execute();
|
||||
});
|
||||
menu.add(openUi);
|
||||
menu.add(openUi); */
|
||||
|
||||
JMenuItem openTimeCheck = new JMenuItem(Translator.INSTANCE.translate("SysTray", "CHECK_TIME_ACCURACY"));
|
||||
openTimeCheck.addActionListener(actionEvent -> {
|
||||
|
@@ -149,6 +149,10 @@ public class Settings {
|
||||
private boolean bootstrap = true;
|
||||
|
||||
|
||||
/** Registered names integrity check */
|
||||
private boolean namesIntegrityCheckEnabled = false;
|
||||
|
||||
|
||||
// Peer-to-peer related
|
||||
private boolean isTestNet = false;
|
||||
/** Port number for inbound peer-to-peer connections. */
|
||||
@@ -639,6 +643,10 @@ public class Settings {
|
||||
return this.blockPruneBatchSize;
|
||||
}
|
||||
|
||||
public boolean isNamesIntegrityCheckEnabled() {
|
||||
return this.namesIntegrityCheckEnabled;
|
||||
}
|
||||
|
||||
|
||||
public boolean isArchiveEnabled() {
|
||||
if (this.topOnly) {
|
||||
|
@@ -12,7 +12,7 @@ import java.util.function.Supplier;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.controller.OnlineAccountsManager;
|
||||
import org.qortal.crosschain.ACCT;
|
||||
import org.qortal.crosschain.SupportedBlockchain;
|
||||
import org.qortal.crypto.Crypto;
|
||||
@@ -47,7 +47,7 @@ public class PresenceTransaction extends Transaction {
|
||||
REWARD_SHARE(0) {
|
||||
@Override
|
||||
public long getLifetime() {
|
||||
return Controller.ONLINE_TIMESTAMP_MODULUS;
|
||||
return OnlineAccountsManager.ONLINE_TIMESTAMP_MODULUS;
|
||||
}
|
||||
},
|
||||
TRADE_BOT(1) {
|
||||
|
55
src/main/resources/i18n/ApiError_fr.properties
Normal file
55
src/main/resources/i18n/ApiError_fr.properties
Normal file
@@ -0,0 +1,55 @@
|
||||
### Commun ###
|
||||
JSON = échec de l'analyse du message JSON
|
||||
INSUFFICIENT_BALANCE = balance insuffisante
|
||||
UNAUTHORIZED = appel de l’API non autorisé
|
||||
REPOSITORY_ISSUE = erreur de dépôt
|
||||
NON_PRODUCTION = cet appel API n'est pas autorisé pour les systèmes en production
|
||||
BLOCKCHAIN_NEEDS_SYNC = la blockchain doit d'abord être synchronisée
|
||||
NO_TIME_SYNC = heure pas encore synchronisée
|
||||
|
||||
### Validation ###
|
||||
INVALID_SIGNATURE = signature invalide
|
||||
INVALID_ADDRESS = adresse invalide
|
||||
INVALID_PUBLIC_KEY = clé publique invalide
|
||||
INVALID_DATA = données invalides
|
||||
INVALID_NETWORK_ADDRESS = adresse réseau invalide
|
||||
ADDRESS_UNKNOWN = adresse de compte inconnue
|
||||
INVALID_CRITERIA = critère de recherche invalide
|
||||
INVALID_REFERENCE = référence invalide
|
||||
TRANSFORMATION_ERROR = ne peut pas transformer JSON en transaction
|
||||
INVALID_PRIVATE_KEY = clé privée invalide
|
||||
INVALID_HEIGHT = hauteur de bloc invalide
|
||||
CANNOT_MINT = le compte ne peut pas mint
|
||||
|
||||
### Blocks ###
|
||||
BLOCK_UNKNOWN = bloc inconnu
|
||||
|
||||
### Transactions ###
|
||||
TRANSACTION_UNKNOWN = opération inconnue
|
||||
PUBLIC_KEY_NOT_FOUND = clé publique introuvable
|
||||
|
||||
# celui-ci est spécial dans le sens où l'appelant doit passer deux chaînes supplémentaires, d'où les deux %s
|
||||
TRANSACTION_INVALID = transaction invalide: %s (%s)
|
||||
|
||||
### Nommage ###
|
||||
NAME_UNKNOWN = nom inconnu
|
||||
|
||||
### Asset ###
|
||||
INVALID_ASSET_ID = identifiant d'actif invalide
|
||||
INVALID_ORDER_ID = identifiant de commande d'actif non valide
|
||||
ORDER_UNKNOWN = identifiant d'ordre d'actif inconnu
|
||||
|
||||
### Groupes ###
|
||||
GROUP_UNKNOWN = groupe inconnu
|
||||
|
||||
### Blockchain étrangère ###
|
||||
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = Problème blokchain étrangère ou de réseau ElectrumX
|
||||
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = solde insuffisant sur la blockchain étrangère
|
||||
FOREIGN_BLOCKCHAIN_TOO_SOON = trop tôt pour diffuser la transaction sur la blockchain étrangère (temps de verrouillage/temps de bloc médian)
|
||||
|
||||
### Portail de trading ###
|
||||
ORDER_SIZE_TOO_SMALL = montant de commande trop bas
|
||||
|
||||
### Données ###
|
||||
FILE_NOT_FOUND = fichier introuvable
|
||||
NO_REPLY = le pair n'a pas renvoyé de données
|
41
src/main/resources/i18n/SysTray_fr.properties
Normal file
41
src/main/resources/i18n/SysTray_fr.properties
Normal file
@@ -0,0 +1,41 @@
|
||||
AUTO_UPDATE = Mise à jour automatique
|
||||
|
||||
APPLYING_UPDATE_AND_RESTARTING = Application de la mise à jour automatique et redémarrage...
|
||||
|
||||
BLOCK_HEIGHT = hauteur
|
||||
|
||||
BUILD_VERSION = Numéro de version
|
||||
|
||||
CHECK_TIME_ACCURACY = Vérifier l'heure
|
||||
|
||||
CONNECTING = Connexion en cours
|
||||
|
||||
CONNECTION = connexion
|
||||
|
||||
CONNECTIONS = connexions
|
||||
|
||||
CREATING_BACKUP_OF_DB_FILES = Création d'une sauvegarde des fichiers de la base de données...
|
||||
|
||||
DB_BACKUP = Sauvegarde de la base de données
|
||||
|
||||
DB_MAINTENANCE = Maintenance de la base de données
|
||||
|
||||
DB_CHECKPOINT = Point de contrôle de la base de données
|
||||
|
||||
EXIT = Quitter
|
||||
|
||||
MINTING_DISABLED = NE mint PAS
|
||||
|
||||
MINTING_ENABLED = \u2714 Minting
|
||||
|
||||
OPEN_UI = Ouvrir l'interface
|
||||
|
||||
PERFORMING_DB_CHECKPOINT = Enregistrement des modifications de base de données non validées...
|
||||
|
||||
PERFORMING_DB_MAINTENANCE = Entrain d'effectuer la maintenance programmée...
|
||||
|
||||
SYNCHRONIZE_CLOCK = Mettre l'heure à jour
|
||||
|
||||
SYNCHRONIZING_BLOCKCHAIN = Synchronisation
|
||||
|
||||
SYNCHRONIZING_CLOCK = Synchronisation de l'heure
|
151
src/main/resources/i18n/TransactionValidity_fr.properties
Normal file
151
src/main/resources/i18n/TransactionValidity_fr.properties
Normal file
@@ -0,0 +1,151 @@
|
||||
OK = OK
|
||||
|
||||
INVALID_ADDRESS = adresse invalide
|
||||
|
||||
NEGATIVE_AMOUNT = montant invalide/négatif
|
||||
|
||||
NEGATIVE_FEE = frais invalides/négatifs
|
||||
|
||||
NO_BALANCE = solde insuffisant
|
||||
|
||||
INVALID_REFERENCE = référence invalide
|
||||
|
||||
INVALID_NAME_LENGTH = longueur de nom invalide
|
||||
|
||||
INVALID_VALUE_LENGTH = longueur de 'valeur' invalide
|
||||
|
||||
NAME_ALREADY_REGISTERED = le nom est déjà enregistré
|
||||
|
||||
NAME_DOES_NOT_EXIST = le nom n'existe pas
|
||||
|
||||
INVALID_NAME_OWNER = le nom du propriétaire est invalide
|
||||
|
||||
NAME_ALREADY_FOR_SALE = le nom est déjà en vente
|
||||
|
||||
NAME_NOT_FOR_SALE = le nom n'est pas à vendre
|
||||
|
||||
BUYER_ALREADY_OWNER = l'acheteur est déjà le propriétaire
|
||||
|
||||
INVALID_AMOUNT = montant invalide
|
||||
|
||||
INVALID_SELLER = vendeur invalide
|
||||
|
||||
NAME_NOT_NORMALIZED = le nom n'est pas sous la forme 'normalisée' Unicode
|
||||
|
||||
INVALID_DESCRIPTION_LENGTH = longueur de description invalide
|
||||
|
||||
INVALID_OPTIONS_COUNT = nombre d'options invalides
|
||||
|
||||
INVALID_OPTION_LENGTH = longueur des options invalide
|
||||
|
||||
DUPLICATE_OPTION = option dupliquée
|
||||
|
||||
POLL_ALREADY_EXISTS = le scrutin existe déjà
|
||||
|
||||
POLL_DOES_NOT_EXIST = le scrutin n'existe pas
|
||||
|
||||
POLL_OPTION_DOES_NOT_EXIST = Ce choix de scrutin n'existe pas
|
||||
|
||||
ALREADY_VOTED_FOR_THAT_OPTION = Vous avez déjà voté pour ce choix
|
||||
|
||||
INVALID_DATA_LENGTH = longueur de données invalide
|
||||
|
||||
INVALID_QUANTITY = quantité invalide
|
||||
|
||||
ASSET_DOES_NOT_EXIST = l'actif n'existe pas
|
||||
|
||||
INVALID_RETURN = retour invalide
|
||||
|
||||
HAVE_EQUALS_WANT = l'actif désiré est le même que l'actif possédé
|
||||
|
||||
ORDER_DOES_NOT_EXIST = l'ordre d'échange d'actifs n'existe pas
|
||||
|
||||
INVALID_ORDER_CREATOR = créateur d'ordre invalide
|
||||
|
||||
INVALID_PAYMENTS_COUNT = nombre de paiements invalides
|
||||
|
||||
NEGATIVE_PRICE = prix invalide/négatif
|
||||
|
||||
INVALID_CREATION_BYTES = octets de création invalides
|
||||
|
||||
INVALID_TAGS_LENGTH = longueur de 'tags' invalide
|
||||
|
||||
INVALID_AT_TYPE_LENGTH = longueur 'type' AT invalide
|
||||
|
||||
INVALID_AT_TRANSACTION = transaction AT invalide
|
||||
|
||||
INSUFFICIENT_FEE = frais insuffisant
|
||||
|
||||
ASSET_DOES_NOT_MATCH_AT = l'actif ne correspond pas à l'actif d'AT
|
||||
ASSET_ALREADY_EXISTS = l'actif existe déjà
|
||||
MISSING_CREATOR = créateur manquant
|
||||
TIMESTAMP_TOO_OLD = horodatage trop ancien
|
||||
TIMESTAMP_TOO_NEW = horodatage trop récent
|
||||
TOO_MANY_UNCONFIRMED = le compte a trop de transactions non confirmées en attente
|
||||
GROUP_ALREADY_EXISTS = le groupe existe déjà
|
||||
GROUP_DOES_NOT_EXIST = le groupe n'existe pas
|
||||
INVALID_GROUP_OWNER = propriétaire de groupe invalide
|
||||
ALREADY_GROUP_MEMBER = vous êtes déjà un(e) membre du groupe
|
||||
GROUP_OWNER_CANNOT_LEAVE = le propriétaire du groupe ne peut pas quitter le groupe
|
||||
NOT_GROUP_MEMBER = le compte n'est pas membre du groupe
|
||||
ALREADY_GROUP_ADMIN = vous êtes déjà l'administrateur(trice) du groupe
|
||||
NOT_GROUP_ADMIN = le compte n'est pas un administrateur du groupe
|
||||
INVALID_LIFETIME = durée de vie invalide
|
||||
INVITE_UNKNOWN = invitation de groupe inconnue
|
||||
BAN_EXISTS = déjà banni
|
||||
BAN_UNKNOWN = bannissement inconnu
|
||||
BANNED_FROM_GROUP = banned from group
|
||||
JOIN_REQUEST_EXISTS = la demande d'adhésion au groupe existe déjà
|
||||
INVALID_GROUP_APPROVAL_THRESHOLD = seuil d'approbation de groupe non valide
|
||||
GROUP_ID_MISMATCH = identifiant de groupe non-concorde
|
||||
INVALID_GROUP_ID = identifiant de groupe invalide
|
||||
TRANSACTION_UNKNOWN = transaction inconnue
|
||||
TRANSACTION_ALREADY_CONFIRMED = la transaction a déjà été confirmée
|
||||
INVALID_TX_GROUP_ID = identifiant du groupe de transactions invalide
|
||||
TX_GROUP_ID_MISMATCH = l'identifiant du groupe de transaction ne correspond pas
|
||||
|
||||
MULTIPLE_NAMES_FORBIDDEN = l'enregistrement de plusieurs noms par compte est interdit
|
||||
|
||||
INVALID_ASSET_OWNER = propriétaire de l'actif invalide
|
||||
|
||||
AT_IS_FINISHED = l'AT est fini
|
||||
|
||||
NO_FLAG_PERMISSION = le compte n'a pas cette autorisation
|
||||
|
||||
NOT_MINTING_ACCOUNT = le compte ne peut pas mint
|
||||
|
||||
REWARD_SHARE_UNKNOWN = partage de récompense inconnu
|
||||
|
||||
INVALID_REWARD_SHARE_PERCENT = pourcentage du partage de récompense invalide
|
||||
|
||||
PUBLIC_KEY_UNKNOWN = clé publique inconnue
|
||||
|
||||
INVALID_PUBLIC_KEY = clé publique invalide
|
||||
|
||||
AT_UNKNOWN = AT inconnu
|
||||
|
||||
AT_ALREADY_EXISTS = AT déjà existante
|
||||
|
||||
GROUP_APPROVAL_NOT_REQUIRED = approbation de groupe non requise
|
||||
|
||||
GROUP_APPROVAL_DECIDED = approbation de groupe déjà décidée
|
||||
|
||||
MAXIMUM_REWARD_SHARES = déjà au nombre maximum de récompense pour ce compte
|
||||
|
||||
TRANSACTION_ALREADY_EXISTS = la transaction existe déjà
|
||||
|
||||
NO_BLOCKCHAIN_LOCK = nœud de la blockchain actuellement occupé
|
||||
ORDER_ALREADY_CLOSED = l'ordre d'échange d'actifs est déjà fermé
|
||||
CLOCK_NOT_SYNCED = horloge non synchronisée
|
||||
ASSET_NOT_SPENDABLE = l'actif n'est pas dépensable
|
||||
ACCOUNT_CANNOT_REWARD_SHARE = le compte ne peut pas récompenser
|
||||
SELF_SHARE_EXISTS = l'auto-partage (récompense) existe déjà
|
||||
ACCOUNT_ALREADY_EXISTS = Le compte existe déjà
|
||||
INVALID_GROUP_BLOCK_DELAY = délai de blocage d'approbation de groupe invalide
|
||||
INCORRECT_NONCE = PoW nonce incorrect
|
||||
INVALID_TIMESTAMP_SIGNATURE = signature d'horodatage invalide
|
||||
ADDRESS_IN_BLACKLIST = cette adresse est dans votre liste noire
|
||||
ADDRESS_ABOVE_RATE_LIMIT = l'adresse a atteint la limite de débit spécifiée
|
||||
DUPLICATE_MESSAGE = l'adresse a envoyé un message en double
|
||||
INVALID_BUT_OK = invalide mais OK
|
||||
NOT_YET_RELEASED = fonctionnalité pas encore publiée
|
@@ -40,7 +40,7 @@ public class BlockApiTests extends ApiCommon {
|
||||
byte[] signatureBytes = GenesisBlock.getInstance(repository).getSignature();
|
||||
String signature = Base58.encode(signatureBytes);
|
||||
|
||||
assertNotNull(this.blocksResource.getBlock(signature));
|
||||
assertNotNull(this.blocksResource.getBlock(signature, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class BlockApiTests extends ApiCommon {
|
||||
|
||||
@Test
|
||||
public void testGetBlockByHeight() {
|
||||
assertNotNull(this.blocksResource.getByHeight(1));
|
||||
assertNotNull(this.blocksResource.getByHeight(1, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -8,7 +8,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.controller.BlockMinter;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.controller.OnlineAccountsManager;
|
||||
import org.qortal.data.account.RewardShareData;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
@@ -77,7 +77,7 @@ public class BlocksMintedCountTests extends Common {
|
||||
assertNotNull(testRewardShareData);
|
||||
|
||||
// Create signed timestamps
|
||||
Controller.getInstance().ensureTestingAccountsOnline(mintingAccount, testRewardShareAccount);
|
||||
OnlineAccountsManager.getInstance().ensureTestingAccountsOnline(mintingAccount, testRewardShareAccount);
|
||||
|
||||
// Even though Alice features in two online reward-shares, she should only gain +1 blocksMinted
|
||||
// Bob only features in one online reward-share, so should also only gain +1 blocksMinted
|
||||
@@ -87,7 +87,7 @@ public class BlocksMintedCountTests extends Common {
|
||||
|
||||
private void testRewardShare(Repository repository, PrivateKeyAccount testRewardShareAccount, int aliceDelta, int bobDelta) throws DataException {
|
||||
// Create signed timestamps
|
||||
Controller.getInstance().ensureTestingAccountsOnline(testRewardShareAccount);
|
||||
OnlineAccountsManager.getInstance().ensureTestingAccountsOnline(testRewardShareAccount);
|
||||
|
||||
testRewardShareRetainingTimestamps(repository, testRewardShareAccount, aliceDelta, bobDelta);
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.controller.BlockMinter;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.controller.OnlineAccountsManager;
|
||||
import org.qortal.data.account.RewardShareData;
|
||||
import org.qortal.data.block.BlockData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
@@ -73,7 +73,7 @@ public class DisagreementTests extends Common {
|
||||
assertNotNull(testRewardShareData);
|
||||
|
||||
// Create signed timestamps
|
||||
Controller.getInstance().ensureTestingAccountsOnline(mintingAccount, testRewardShareAccount);
|
||||
OnlineAccountsManager.getInstance().ensureTestingAccountsOnline(mintingAccount, testRewardShareAccount);
|
||||
|
||||
// Mint another block
|
||||
BlockMinter.mintTestingBlockRetainingTimestamps(repository, mintingAccount);
|
||||
|
Reference in New Issue
Block a user