forked from Qortal/qortal
Merge branch 'EPC-fixes' into lite-node
This commit is contained in:
commit
e7ee3a06c7
@ -17,10 +17,10 @@
|
|||||||
<ROW Property="Manufacturer" Value="Qortal"/>
|
<ROW Property="Manufacturer" Value="Qortal"/>
|
||||||
<ROW Property="MsiLogging" MultiBuildValue="DefaultBuild:vp"/>
|
<ROW Property="MsiLogging" MultiBuildValue="DefaultBuild:vp"/>
|
||||||
<ROW Property="NTP_GOOD" Value="false"/>
|
<ROW Property="NTP_GOOD" Value="false"/>
|
||||||
<ROW Property="ProductCode" Value="1033:{DEA09B3D-AFFA-409F-B208-E148E9A9005D} 1049:{79180B3D-8A6B-4DED-BD60-1A58F941E1DE} 2052:{90F65B96-22CD-41FA-82B0-E65183EA1EF9} 2057:{AB4872AC-E794-42BD-9305-8DFD06243A88} " Type="16"/>
|
<ROW Property="ProductCode" Value="1033:{8C9CFB9D-BC4C-4142-A5A5-5551BF3B9467} 1049:{4A5BDDD9-ED71-431A-A46F-D19E9DE17216} 2052:{0B9DCE00-BE23-434D-BD6A-1CFA6AB3CA43} 2057:{23D81967-556A-41B8-9981-A739E2820624} " Type="16"/>
|
||||||
<ROW Property="ProductLanguage" Value="2057"/>
|
<ROW Property="ProductLanguage" Value="2057"/>
|
||||||
<ROW Property="ProductName" Value="Qortal"/>
|
<ROW Property="ProductName" Value="Qortal"/>
|
||||||
<ROW Property="ProductVersion" Value="3.2.3" Type="32"/>
|
<ROW Property="ProductVersion" Value="3.2.5" Type="32"/>
|
||||||
<ROW Property="RECONFIG_NTP" Value="true"/>
|
<ROW Property="RECONFIG_NTP" Value="true"/>
|
||||||
<ROW Property="REMOVE_BLOCKCHAIN" Value="YES" Type="4"/>
|
<ROW Property="REMOVE_BLOCKCHAIN" Value="YES" Type="4"/>
|
||||||
<ROW Property="REPAIR_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_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_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="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="{63FD92A7-4AE2-46A0-9B83-EB27DA636C65}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
<ROW Component="AI_CustomARPName" ComponentId="{D5A1DC7D-914F-4425-8BA6-A1AE05D0F361}" 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="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="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"/>
|
<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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.qortal</groupId>
|
<groupId>org.qortal</groupId>
|
||||||
<artifactId>qortal</artifactId>
|
<artifactId>qortal</artifactId>
|
||||||
<version>3.2.3</version>
|
<version>3.2.5</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<properties>
|
<properties>
|
||||||
<skipTests>true</skipTests>
|
<skipTests>true</skipTests>
|
||||||
|
@ -394,6 +394,10 @@ public class AdminResource {
|
|||||||
) @QueryParam("limit") Integer limit, @Parameter(
|
) @QueryParam("limit") Integer limit, @Parameter(
|
||||||
ref = "offset"
|
ref = "offset"
|
||||||
) @QueryParam("offset") Integer offset, @Parameter(
|
) @QueryParam("offset") Integer offset, @Parameter(
|
||||||
|
name = "tail",
|
||||||
|
description = "Fetch most recent log lines",
|
||||||
|
schema = @Schema(type = "boolean")
|
||||||
|
) @QueryParam("tail") Boolean tail, @Parameter(
|
||||||
ref = "reverse"
|
ref = "reverse"
|
||||||
) @QueryParam("reverse") Boolean reverse) {
|
) @QueryParam("reverse") Boolean reverse) {
|
||||||
LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
|
LoggerContext loggerContext = (LoggerContext) LogManager.getContext();
|
||||||
@ -409,6 +413,13 @@ public class AdminResource {
|
|||||||
if (reverse != null && reverse)
|
if (reverse != null && reverse)
|
||||||
logLines = Lists.reverse(logLines);
|
logLines = Lists.reverse(logLines);
|
||||||
|
|
||||||
|
// Tail mode - return the last X lines (where X = limit)
|
||||||
|
if (tail != null && tail) {
|
||||||
|
if (limit != null && limit > 0) {
|
||||||
|
offset = logLines.size() - limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// offset out of bounds?
|
// offset out of bounds?
|
||||||
if (offset != null && (offset < 0 || offset >= logLines.size()))
|
if (offset != null && (offset < 0 || offset >= logLines.size()))
|
||||||
return "";
|
return "";
|
||||||
@ -429,7 +440,7 @@ public class AdminResource {
|
|||||||
|
|
||||||
limit = Math.min(limit, logLines.size());
|
limit = Math.min(limit, logLines.size());
|
||||||
|
|
||||||
logLines.subList(limit - 1, logLines.size()).clear();
|
logLines.subList(limit, logLines.size()).clear();
|
||||||
|
|
||||||
return String.join("\n", logLines);
|
return String.join("\n", logLines);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -93,10 +93,12 @@ public class ArbitraryDataFile {
|
|||||||
File outputFile = outputFilePath.toFile();
|
File outputFile = outputFilePath.toFile();
|
||||||
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
|
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) {
|
||||||
outputStream.write(fileContent);
|
outputStream.write(fileContent);
|
||||||
|
outputStream.close();
|
||||||
this.filePath = outputFilePath;
|
this.filePath = outputFilePath;
|
||||||
// Verify hash
|
// Verify hash
|
||||||
if (!this.hash58.equals(this.digest58())) {
|
String digest58 = this.digest58();
|
||||||
LOGGER.error("Hash {} does not match file digest {} for signature: {}", this.hash58, this.digest58(), Base58.encode(signature));
|
if (!this.hash58.equals(digest58)) {
|
||||||
|
LOGGER.error("Hash {} does not match file digest {} for signature: {}", this.hash58, digest58, Base58.encode(signature));
|
||||||
this.delete();
|
this.delete();
|
||||||
throw new DataException("Data file digest validation failed");
|
throw new DataException("Data file digest validation failed");
|
||||||
}
|
}
|
||||||
|
@ -430,9 +430,8 @@ public class BlockChain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getNameRegistrationUnitFeeAtTimestamp(long ourTimestamp) {
|
public long getNameRegistrationUnitFeeAtTimestamp(long ourTimestamp) {
|
||||||
// Scan through for reward at our height
|
for (int i = nameRegistrationUnitFees.size() - 1; i >= 0; --i)
|
||||||
for (int i = 0; i < nameRegistrationUnitFees.size(); ++i)
|
if (nameRegistrationUnitFees.get(i).timestamp <= ourTimestamp)
|
||||||
if (ourTimestamp >= nameRegistrationUnitFees.get(i).timestamp)
|
|
||||||
return nameRegistrationUnitFees.get(i).fee;
|
return nameRegistrationUnitFees.get(i).fee;
|
||||||
|
|
||||||
// Default to system-wide unit fee
|
// Default to system-wide unit fee
|
||||||
|
@ -224,7 +224,7 @@ public class BlockMinter extends Thread {
|
|||||||
// The last iteration found a higher weight block in the network, so sleep for a while
|
// The last iteration found a higher weight block in the network, so sleep for a while
|
||||||
// to allow is to sync the higher weight chain. We are sleeping here rather than when
|
// to allow is to sync the higher weight chain. We are sleeping here rather than when
|
||||||
// detected as we don't want to hold the blockchain lock open.
|
// detected as we don't want to hold the blockchain lock open.
|
||||||
LOGGER.info("Sleeping for 10 seconds...");
|
LOGGER.debug("Sleeping for 10 seconds...");
|
||||||
Thread.sleep(10 * 1000L);
|
Thread.sleep(10 * 1000L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,13 +333,13 @@ public class BlockMinter extends Thread {
|
|||||||
// If less than 30 seconds has passed since first detection the higher weight chain,
|
// If less than 30 seconds has passed since first detection the higher weight chain,
|
||||||
// we should skip our block submission to give us the opportunity to sync to the better chain
|
// we should skip our block submission to give us the opportunity to sync to the better chain
|
||||||
if (NTP.getTime() - timeOfLastLowWeightBlock < 30*1000L) {
|
if (NTP.getTime() - timeOfLastLowWeightBlock < 30*1000L) {
|
||||||
LOGGER.info("Higher weight chain found in peers, so not signing a block this round");
|
LOGGER.debug("Higher weight chain found in peers, so not signing a block this round");
|
||||||
LOGGER.info("Time since detected: {}", NTP.getTime() - timeOfLastLowWeightBlock);
|
LOGGER.debug("Time since detected: {}ms", NTP.getTime() - timeOfLastLowWeightBlock);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// More than 30 seconds have passed, so we should submit our block candidate anyway.
|
// More than 30 seconds have passed, so we should submit our block candidate anyway.
|
||||||
LOGGER.info("More than 30 seconds passed, so proceeding to submit block candidate...");
|
LOGGER.debug("More than 30 seconds passed, so proceeding to submit block candidate...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -628,15 +628,20 @@ public class Controller extends Thread {
|
|||||||
MessageType.INFO);
|
MessageType.INFO);
|
||||||
|
|
||||||
LOGGER.info("Starting scheduled repository maintenance. This can take a while...");
|
LOGGER.info("Starting scheduled repository maintenance. This can take a while...");
|
||||||
|
int attempts = 0;
|
||||||
|
while (attempts <= 5) {
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
attempts++;
|
||||||
|
|
||||||
// Timeout if the database isn't ready for maintenance after 60 seconds
|
// Timeout if the database isn't ready for maintenance after 60 seconds
|
||||||
long timeout = 60 * 1000L;
|
long timeout = 60 * 1000L;
|
||||||
repository.performPeriodicMaintenance(timeout);
|
repository.performPeriodicMaintenance(timeout);
|
||||||
|
|
||||||
LOGGER.info("Scheduled repository maintenance completed");
|
LOGGER.info("Scheduled repository maintenance completed");
|
||||||
|
break;
|
||||||
} catch (DataException | TimeoutException e) {
|
} catch (DataException | TimeoutException e) {
|
||||||
LOGGER.error("Scheduled repository maintenance failed", e);
|
LOGGER.info("Scheduled repository maintenance failed. Retrying up to 5 times...", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a new random interval
|
// Get a new random interval
|
||||||
@ -710,29 +715,6 @@ public class Controller extends Thread {
|
|||||||
return lastMisbehaved != null && lastMisbehaved > NTP.getTime() - MISBEHAVIOUR_COOLOFF;
|
return lastMisbehaved != null && lastMisbehaved > NTP.getTime() - MISBEHAVIOUR_COOLOFF;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** True if peer has unknown height, lower height or same height and same block signature (unless we don't have their block signature). */
|
|
||||||
public static Predicate<Peer> hasShorterBlockchain = peer -> {
|
|
||||||
BlockData highestBlockData = getInstance().getChainTip();
|
|
||||||
int ourHeight = highestBlockData.getHeight();
|
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
|
||||||
|
|
||||||
// Ensure we have chain tip data for this peer
|
|
||||||
if (peerChainTipData == null)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Remove if peer is at a lower height than us
|
|
||||||
Integer peerHeight = peerChainTipData.getLastHeight();
|
|
||||||
if (peerHeight == null || peerHeight < ourHeight)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Don't remove if peer is on a greater height chain than us, or if we don't have their block signature
|
|
||||||
if (peerHeight > ourHeight || peerChainTipData.getLastBlockSignature() == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Remove if signatures match
|
|
||||||
return Arrays.equals(peerChainTipData.getLastBlockSignature(), highestBlockData.getSignature());
|
|
||||||
};
|
|
||||||
|
|
||||||
public static final Predicate<Peer> hasNoRecentBlock = peer -> {
|
public static final Predicate<Peer> hasNoRecentBlock = peer -> {
|
||||||
final Long minLatestBlockTimestamp = getMinimumLatestBlockTimestamp();
|
final Long minLatestBlockTimestamp = getMinimumLatestBlockTimestamp();
|
||||||
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
final PeerChainTipData peerChainTipData = peer.getChainTipData();
|
||||||
|
@ -240,9 +240,6 @@ public class Synchronizer extends Thread {
|
|||||||
// Disregard peers that are on the same block as last sync attempt and we didn't like their chain
|
// Disregard peers that are on the same block as last sync attempt and we didn't like their chain
|
||||||
peers.removeIf(Controller.hasInferiorChainTip);
|
peers.removeIf(Controller.hasInferiorChainTip);
|
||||||
|
|
||||||
// Remove peers with unknown height, lower height or same height and same block signature (unless we don't have their block signature)
|
|
||||||
peers.removeIf(Controller.hasShorterBlockchain);
|
|
||||||
|
|
||||||
final int peersBeforeComparison = peers.size();
|
final int peersBeforeComparison = peers.size();
|
||||||
|
|
||||||
// Request recent block summaries from the remaining peers, and locate our common block with each
|
// Request recent block summaries from the remaining peers, and locate our common block with each
|
||||||
|
@ -47,12 +47,12 @@ public class Gui {
|
|||||||
this.splashFrame = SplashFrame.getInstance();
|
this.splashFrame = SplashFrame.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static BufferedImage loadImage(String resourceName) throws IOException {
|
protected static BufferedImage loadImage(String resourceName) {
|
||||||
try (InputStream in = Gui.class.getResourceAsStream("/images/" + resourceName)) {
|
try (InputStream in = Gui.class.getResourceAsStream("/images/" + resourceName)) {
|
||||||
return ImageIO.read(in);
|
return ImageIO.read(in);
|
||||||
} catch (IllegalArgumentException | IOException | ServiceConfigurationError e) {
|
} catch (IllegalArgumentException | IOException | ServiceConfigurationError e) {
|
||||||
LOGGER.warn(String.format("Couldn't locate image resource \"images/%s\"", resourceName));
|
LOGGER.warn(String.format("Couldn't locate image resource \"images/%s\"", resourceName));
|
||||||
throw new IOException(String.format("Couldn't locate image resource \"images/%s\"", resourceName));
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package org.qortal.gui;
|
package org.qortal.gui;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
@ -30,23 +29,18 @@ public class SplashFrame {
|
|||||||
private JLabel statusLabel;
|
private JLabel statusLabel;
|
||||||
|
|
||||||
public SplashPanel() {
|
public SplashPanel() {
|
||||||
try {
|
|
||||||
image = Gui.loadImage(defaultSplash);
|
image = Gui.loadImage(defaultSplash);
|
||||||
|
|
||||||
// Add logo
|
|
||||||
JLabel imageLabel = new JLabel(new ImageIcon(image));
|
|
||||||
imageLabel.setSize(new Dimension(300, 300));
|
|
||||||
add(imageLabel);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
LOGGER.warn("Unable to load splash panel image");
|
|
||||||
}
|
|
||||||
|
|
||||||
setOpaque(true);
|
setOpaque(true);
|
||||||
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
|
||||||
setBorder(new EmptyBorder(10, 10, 10, 10));
|
setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
setBackground(Color.BLACK);
|
setBackground(Color.BLACK);
|
||||||
|
|
||||||
|
// Add logo
|
||||||
|
JLabel imageLabel = new JLabel(new ImageIcon(image));
|
||||||
|
imageLabel.setSize(new Dimension(300, 300));
|
||||||
|
add(imageLabel);
|
||||||
|
|
||||||
// Add spacing
|
// Add spacing
|
||||||
add(Box.createRigidArea(new Dimension(0, 16)));
|
add(Box.createRigidArea(new Dimension(0, 16)));
|
||||||
|
|
||||||
@ -81,7 +75,6 @@ public class SplashFrame {
|
|||||||
|
|
||||||
this.splashDialog = new JFrame();
|
this.splashDialog = new JFrame();
|
||||||
|
|
||||||
try {
|
|
||||||
List<Image> icons = new ArrayList<>();
|
List<Image> icons = new ArrayList<>();
|
||||||
icons.add(Gui.loadImage("icons/icon16.png"));
|
icons.add(Gui.loadImage("icons/icon16.png"));
|
||||||
icons.add(Gui.loadImage("icons/qortal_ui_tray_synced.png"));
|
icons.add(Gui.loadImage("icons/qortal_ui_tray_synced.png"));
|
||||||
@ -91,10 +84,6 @@ public class SplashFrame {
|
|||||||
icons.add(Gui.loadImage("icons/icon64.png"));
|
icons.add(Gui.loadImage("icons/icon64.png"));
|
||||||
icons.add(Gui.loadImage("icons/Qlogo_128.png"));
|
icons.add(Gui.loadImage("icons/Qlogo_128.png"));
|
||||||
this.splashDialog.setIconImages(icons);
|
this.splashDialog.setIconImages(icons);
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
LOGGER.warn("Unable to load splash frame icons");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.splashPanel = new SplashPanel();
|
this.splashPanel = new SplashPanel();
|
||||||
this.splashDialog.getContentPane().add(this.splashPanel);
|
this.splashDialog.getContentPane().add(this.splashPanel);
|
||||||
|
@ -61,13 +61,7 @@ public class SysTray {
|
|||||||
this.popupMenu = createJPopupMenu();
|
this.popupMenu = createJPopupMenu();
|
||||||
|
|
||||||
// Build TrayIcon without AWT PopupMenu (which doesn't support Unicode)...
|
// Build TrayIcon without AWT PopupMenu (which doesn't support Unicode)...
|
||||||
try {
|
|
||||||
this.trayIcon = new TrayIcon(Gui.loadImage("icons/qortal_ui_tray_synced.png"), "qortal", null);
|
this.trayIcon = new TrayIcon(Gui.loadImage("icons/qortal_ui_tray_synced.png"), "qortal", null);
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
LOGGER.warn("Unable to load system tray icon");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// ...and attach mouse listener instead so we can use JPopupMenu (which does support Unicode)
|
// ...and attach mouse listener instead so we can use JPopupMenu (which does support Unicode)
|
||||||
this.trayIcon.addMouseListener(new MouseAdapter() {
|
this.trayIcon.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
"maxBytesPerUnitFee": 1024,
|
"maxBytesPerUnitFee": 1024,
|
||||||
"unitFee": "0.001",
|
"unitFee": "0.001",
|
||||||
"nameRegistrationUnitFees": [
|
"nameRegistrationUnitFees": [
|
||||||
{ "timestamp": 1645372800000, "fee": "5" }
|
{ "timestamp": 1645372800000, "fee": "5" },
|
||||||
|
{ "timestamp": 1651420800000, "fee": "1.25" }
|
||||||
],
|
],
|
||||||
"useBrokenMD160ForAddresses": false,
|
"useBrokenMD160ForAddresses": false,
|
||||||
"requireGroupForApproval": false,
|
"requireGroupForApproval": false,
|
||||||
|
@ -356,8 +356,15 @@ public class MiscTests extends Common {
|
|||||||
UnitFeesByTimestamp pastFeeIncrease = new UnitFeesByTimestamp();
|
UnitFeesByTimestamp pastFeeIncrease = new UnitFeesByTimestamp();
|
||||||
pastFeeIncrease.timestamp = now - 1000L; // 1 second ago
|
pastFeeIncrease.timestamp = now - 1000L; // 1 second ago
|
||||||
pastFeeIncrease.fee = new AmountTypeAdapter().unmarshal("3");
|
pastFeeIncrease.fee = new AmountTypeAdapter().unmarshal("3");
|
||||||
FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFees", Arrays.asList(pastFeeIncrease), true);
|
|
||||||
|
// Set another increase in the future
|
||||||
|
futureFeeIncrease = new UnitFeesByTimestamp();
|
||||||
|
futureFeeIncrease.timestamp = now + (60 * 60 * 1000L); // 1 hour in the future
|
||||||
|
futureFeeIncrease.fee = new AmountTypeAdapter().unmarshal("10");
|
||||||
|
|
||||||
|
FieldUtils.writeField(BlockChain.getInstance(), "nameRegistrationUnitFees", Arrays.asList(pastFeeIncrease, futureFeeIncrease), true);
|
||||||
assertEquals(pastFeeIncrease.fee, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(pastFeeIncrease.timestamp));
|
assertEquals(pastFeeIncrease.fee, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(pastFeeIncrease.timestamp));
|
||||||
|
assertEquals(futureFeeIncrease.fee, BlockChain.getInstance().getNameRegistrationUnitFeeAtTimestamp(futureFeeIncrease.timestamp));
|
||||||
|
|
||||||
// Register a different name
|
// Register a different name
|
||||||
// First try with the default unit fee
|
// First try with the default unit fee
|
||||||
|
Loading…
x
Reference in New Issue
Block a user