forked from Qortal/qortal
Merge branch 'master' into q-apps
This commit is contained in:
commit
64cd21b0dd
6
.github/workflows/pr-testing.yml
vendored
6
.github/workflows/pr-testing.yml
vendored
@ -8,16 +8,16 @@ jobs:
|
||||
mavenTesting:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
- name: Set up the Java JDK
|
||||
uses: actions/setup-java@v2
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
|
@ -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:{6C93A96C-E3AF-42FD-BE11-7EC3734905C6} 1049:{754F5347-82E5-4251-AED0-F4141CDD11F5} 2052:{413BD7B3-A3F8-47D0-BCA4-5C7694A40936} 2057:{71450AC8-1E6F-4469-852D-0591FA693680} " Type="16"/>
|
||||
<ROW Property="ProductCode" Value="1033:{CB85115E-ECCE-4B3D-BB7F-6251A2764922} 1049:{09AC1C62-4E33-4312-826A-38F597ED1B17} 2052:{3CF701B3-E118-4A31-A4B7-156CEA19FBCC} 2057:{468F337D-0EF8-41D1-B5DE-4EEE66BA2AF6} " Type="16"/>
|
||||
<ROW Property="ProductLanguage" Value="2057"/>
|
||||
<ROW Property="ProductName" Value="Qortal"/>
|
||||
<ROW Property="ProductVersion" Value="3.8.3" Type="32"/>
|
||||
<ROW Property="ProductVersion" Value="3.8.5" 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="{EC7B4AD9-F2D9-48C4-A586-C4697D9C380C}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
||||
<ROW Component="AI_CustomARPName" ComponentId="{094B5D07-2258-4A39-9917-2E2F7F6E210B}" 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"/>
|
||||
|
3
pom.xml
3
pom.xml
@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.qortal</groupId>
|
||||
<artifactId>qortal</artifactId>
|
||||
<version>3.8.4</version>
|
||||
<version>3.8.9</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<skipTests>true</skipTests>
|
||||
@ -304,6 +304,7 @@
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.qortal.controller.Controller</mainClass>
|
||||
<manifestEntries>
|
||||
<Multi-Release>true</Multi-Release>
|
||||
<Class-Path>. ..</Class-Path>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@ -35,6 +36,37 @@ public class CrossChainBitcoinResource {
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/height")
|
||||
@Operation(
|
||||
summary = "Returns current Bitcoin block height",
|
||||
description = "Returns the height of the most recent block in the Bitcoin chain.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
public String getBitcoinHeight() {
|
||||
Bitcoin bitcoin = Bitcoin.getInstance();
|
||||
|
||||
try {
|
||||
Integer height = bitcoin.getBlockchainHeight();
|
||||
if (height == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
|
||||
return height.toString();
|
||||
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/walletbalance")
|
||||
@Operation(
|
||||
@ -118,6 +150,45 @@ public class CrossChainBitcoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
description = "BIP32 'm' private/public key in base58",
|
||||
example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public String getUnusedBitcoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Bitcoin bitcoin = Bitcoin.getInstance();
|
||||
|
||||
if (!bitcoin.isValidDeterministicKey(key58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return bitcoin.getUnusedReceiveAddress(key58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/send")
|
||||
@Operation(
|
||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@ -35,6 +36,37 @@ public class CrossChainDigibyteResource {
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/height")
|
||||
@Operation(
|
||||
summary = "Returns current Digibyte block height",
|
||||
description = "Returns the height of the most recent block in the Digibyte chain.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
public String getDigibyteHeight() {
|
||||
Digibyte digibyte = Digibyte.getInstance();
|
||||
|
||||
try {
|
||||
Integer height = digibyte.getBlockchainHeight();
|
||||
if (height == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
|
||||
return height.toString();
|
||||
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/walletbalance")
|
||||
@Operation(
|
||||
@ -118,6 +150,45 @@ public class CrossChainDigibyteResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
description = "BIP32 'm' private/public key in base58",
|
||||
example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public String getUnusedDigibyteReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Digibyte digibyte = Digibyte.getInstance();
|
||||
|
||||
if (!digibyte.isValidDeterministicKey(key58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return digibyte.getUnusedReceiveAddress(key58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/send")
|
||||
@Operation(
|
||||
|
@ -21,6 +21,7 @@ import org.qortal.crosschain.SimpleTransaction;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@ -33,6 +34,37 @@ public class CrossChainDogecoinResource {
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/height")
|
||||
@Operation(
|
||||
summary = "Returns current Dogecoin block height",
|
||||
description = "Returns the height of the most recent block in the Dogecoin chain.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
public String getDogecoinHeight() {
|
||||
Dogecoin dogecoin = Dogecoin.getInstance();
|
||||
|
||||
try {
|
||||
Integer height = dogecoin.getBlockchainHeight();
|
||||
if (height == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
|
||||
return height.toString();
|
||||
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/walletbalance")
|
||||
@Operation(
|
||||
@ -116,6 +148,45 @@ public class CrossChainDogecoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
description = "BIP32 'm' private/public key in base58",
|
||||
example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public String getUnusedDogecoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Dogecoin dogecoin = Dogecoin.getInstance();
|
||||
|
||||
if (!dogecoin.isValidDeterministicKey(key58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return dogecoin.getUnusedReceiveAddress(key58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/send")
|
||||
@Operation(
|
||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@ -35,6 +36,37 @@ public class CrossChainLitecoinResource {
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/height")
|
||||
@Operation(
|
||||
summary = "Returns current Litecoin block height",
|
||||
description = "Returns the height of the most recent block in the Litecoin chain.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
public String getLitecoinHeight() {
|
||||
Litecoin litecoin = Litecoin.getInstance();
|
||||
|
||||
try {
|
||||
Integer height = litecoin.getBlockchainHeight();
|
||||
if (height == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
|
||||
return height.toString();
|
||||
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/walletbalance")
|
||||
@Operation(
|
||||
@ -118,6 +150,45 @@ public class CrossChainLitecoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
description = "BIP32 'm' private/public key in base58",
|
||||
example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public String getUnusedLitecoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Litecoin litecoin = Litecoin.getInstance();
|
||||
|
||||
if (!litecoin.isValidDeterministicKey(key58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return litecoin.getUnusedReceiveAddress(key58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/send")
|
||||
@Operation(
|
||||
|
@ -20,6 +20,7 @@ import org.qortal.crosschain.SimpleTransaction;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@ -32,6 +33,37 @@ public class CrossChainPirateChainResource {
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/height")
|
||||
@Operation(
|
||||
summary = "Returns current PirateChain block height",
|
||||
description = "Returns the height of the most recent block in the PirateChain chain.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
public String getPirateChainHeight() {
|
||||
PirateChain pirateChain = PirateChain.getInstance();
|
||||
|
||||
try {
|
||||
Integer height = pirateChain.getBlockchainHeight();
|
||||
if (height == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
|
||||
return height.toString();
|
||||
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/walletbalance")
|
||||
@Operation(
|
||||
|
@ -14,6 +14,7 @@ import java.util.List;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.ws.rs.HeaderParam;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
@ -35,6 +36,37 @@ public class CrossChainRavencoinResource {
|
||||
@Context
|
||||
HttpServletRequest request;
|
||||
|
||||
@GET
|
||||
@Path("/height")
|
||||
@Operation(
|
||||
summary = "Returns current Ravencoin block height",
|
||||
description = "Returns the height of the most recent block in the Ravencoin chain.",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
schema = @Schema(
|
||||
type = "number"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
public String getRavencoinHeight() {
|
||||
Ravencoin ravencoin = Ravencoin.getInstance();
|
||||
|
||||
try {
|
||||
Integer height = ravencoin.getBlockchainHeight();
|
||||
if (height == null)
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
|
||||
return height.toString();
|
||||
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/walletbalance")
|
||||
@Operation(
|
||||
@ -118,6 +150,45 @@ public class CrossChainRavencoinResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/unusedaddress")
|
||||
@Operation(
|
||||
summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
|
||||
description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string",
|
||||
description = "BIP32 'm' private/public key in base58",
|
||||
example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public String getUnusedRavencoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
Ravencoin ravencoin = Ravencoin.getInstance();
|
||||
|
||||
if (!ravencoin.isValidDeterministicKey(key58))
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
|
||||
|
||||
try {
|
||||
return ravencoin.getUnusedReceiveAddress(key58);
|
||||
} catch (ForeignBlockchainException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/send")
|
||||
@Operation(
|
||||
|
@ -222,6 +222,42 @@ public class AdminResource {
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/summary/alltime")
|
||||
@Operation(
|
||||
summary = "Summary of activity since genesis",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(schema = @Schema(implementation = ActivitySummary.class))
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
@SecurityRequirement(name = "apiKey")
|
||||
public ActivitySummary allTimeSummary(@HeaderParam(Security.API_KEY_HEADER) String apiKey) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
ActivitySummary summary = new ActivitySummary();
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
int startHeight = 1;
|
||||
long start = repository.getBlockRepository().fromHeight(startHeight).getTimestamp();
|
||||
int endHeight = repository.getBlockRepository().getBlockchainHeight();
|
||||
|
||||
summary.setBlockCount(endHeight - startHeight);
|
||||
|
||||
summary.setTransactionCountByType(repository.getTransactionRepository().getTransactionSummary(startHeight + 1, endHeight));
|
||||
|
||||
summary.setAssetsIssued(repository.getAssetRepository().getRecentAssetIds(start).size());
|
||||
|
||||
summary.setNamesRegistered (repository.getNameRepository().getRecentNames(start).size());
|
||||
|
||||
return summary;
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/enginestats")
|
||||
@Operation(
|
||||
|
@ -60,7 +60,7 @@ public class BootstrapResource {
|
||||
bootstrap.validateBlockchain();
|
||||
return bootstrap.create();
|
||||
|
||||
} catch (DataException | InterruptedException | IOException e) {
|
||||
} catch (Exception e) {
|
||||
LOGGER.info("Unable to create bootstrap", e);
|
||||
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.REPOSITORY_ISSUE, e.getMessage());
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ public enum Service {
|
||||
QCHAT_IMAGE(420, true, 500*1024L, null),
|
||||
VIDEO(500, false, null, null),
|
||||
AUDIO(600, false, null, null),
|
||||
QCHAT_AUDIO(610, true, 10*1024*1024L, null),
|
||||
QCHAT_VOICE(620, true, 10*1024*1024L, null),
|
||||
BLOG(700, false, null, null),
|
||||
BLOG_POST(777, false, null, null),
|
||||
BLOG_COMMENT(778, false, null, null),
|
||||
|
@ -204,7 +204,7 @@ public class ArbitraryDataCleanupManager extends Thread {
|
||||
|
||||
if (completeFileExists && !allChunksExist) {
|
||||
// We have the complete file but not the chunks, so let's convert it
|
||||
LOGGER.info(String.format("Transaction %s has complete file but no chunks",
|
||||
LOGGER.debug(String.format("Transaction %s has complete file but no chunks",
|
||||
Base58.encode(arbitraryTransactionData.getSignature())));
|
||||
|
||||
ArbitraryTransactionUtils.convertFileToChunks(arbitraryTransactionData, now, STALE_FILE_TIMEOUT);
|
||||
|
@ -39,9 +39,10 @@ public class AtStatesPruner implements Runnable {
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
int pruneStartHeight = repository.getATRepository().getAtPruneHeight();
|
||||
int maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
|
||||
|
||||
repository.discardChanges();
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
|
||||
repository.saveChanges();
|
||||
|
||||
while (!Controller.isStopping()) {
|
||||
@ -92,7 +93,8 @@ public class AtStatesPruner implements Runnable {
|
||||
if (upperPrunableHeight > upperBatchHeight) {
|
||||
pruneStartHeight = upperBatchHeight;
|
||||
repository.getATRepository().setAtPruneHeight(pruneStartHeight);
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
|
||||
repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
|
||||
repository.saveChanges();
|
||||
|
||||
final int finalPruneStartHeight = pruneStartHeight;
|
||||
|
@ -26,9 +26,10 @@ public class AtStatesTrimmer implements Runnable {
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
int trimStartHeight = repository.getATRepository().getAtTrimHeight();
|
||||
int maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
|
||||
|
||||
repository.discardChanges();
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
|
||||
repository.saveChanges();
|
||||
|
||||
while (!Controller.isStopping()) {
|
||||
@ -70,7 +71,8 @@ public class AtStatesTrimmer implements Runnable {
|
||||
if (upperTrimmableHeight > upperBatchHeight) {
|
||||
trimStartHeight = upperBatchHeight;
|
||||
repository.getATRepository().setAtTrimHeight(trimStartHeight);
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
|
||||
repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
|
||||
repository.saveChanges();
|
||||
|
||||
final int finalTrimStartHeight = trimStartHeight;
|
||||
|
@ -157,4 +157,18 @@ public class PruneManager {
|
||||
return (height < latestUnprunedHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* When rebuilding the latest AT states, we need to specify a maxHeight, so that we aren't tracking
|
||||
* very recent AT states that could potentially be orphaned. This method ensures that AT states
|
||||
* are given a sufficient number of blocks to confirm before being tracked as a latest AT state.
|
||||
*/
|
||||
public static int getMaxHeightForLatestAtStates(Repository repository) throws DataException {
|
||||
// Get current chain height, and subtract a certain number of "confirmation" blocks
|
||||
// This is to ensure we are basing our latest AT states data on confirmed blocks -
|
||||
// ones that won't be orphaned in any normal circumstances
|
||||
final int confirmationBlocks = 250;
|
||||
final int chainHeight = repository.getBlockRepository().getBlockchainHeight();
|
||||
return chainHeight - confirmationBlocks;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ public class Bitcoin extends Bitcoiny {
|
||||
//CLOSED new Server("bitcoin.grey.pw", Server.ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("btc.litepay.ch", Server.ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("electrum.pabu.io", Server.ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("electrumx.dev", Server.ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("electrumx.hodlwallet.com", Server.ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("gd42.org", Server.ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("korea.electrum-server.com", Server.ConnectionType.SSL, 50002),
|
||||
@ -56,28 +57,75 @@ public class Bitcoin extends Bitcoiny {
|
||||
//1.15.0 new Server("alviss.coinjoined.com", Server.ConnectionType.SSL, 50002),
|
||||
//1.15.0 new Server("electrum.acinq.co", Server.ConnectionType.SSL, 50002),
|
||||
//1.14.0 new Server("electrum.coinext.com.br", Server.ConnectionType.SSL, 50002),
|
||||
//F1.7.0 new Server("btc.lastingcoin.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("104.248.139.211", Server.ConnectionType.SSL, 50002),
|
||||
new Server("128.0.190.26", Server.ConnectionType.SSL, 50002),
|
||||
new Server("142.93.6.38", Server.ConnectionType.SSL, 50002),
|
||||
new Server("157.245.172.236", Server.ConnectionType.SSL, 50002),
|
||||
new Server("167.172.226.175", Server.ConnectionType.SSL, 50002),
|
||||
new Server("167.172.42.31", Server.ConnectionType.SSL, 50002),
|
||||
new Server("178.62.80.20", Server.ConnectionType.SSL, 50002),
|
||||
new Server("185.64.116.15", Server.ConnectionType.SSL, 50002),
|
||||
new Server("188.165.206.215", Server.ConnectionType.SSL, 50002),
|
||||
new Server("188.165.211.112", Server.ConnectionType.SSL, 50002),
|
||||
new Server("2azzarita.hopto.org", Server.ConnectionType.SSL, 50002),
|
||||
new Server("2electrumx.hopto.me", Server.ConnectionType.SSL, 56022),
|
||||
new Server("2ex.digitaleveryware.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("65.39.140.37", Server.ConnectionType.SSL, 50002),
|
||||
new Server("68.183.188.105", Server.ConnectionType.SSL, 50002),
|
||||
new Server("71.73.14.254", Server.ConnectionType.SSL, 50002),
|
||||
new Server("94.23.247.135", Server.ConnectionType.SSL, 50002),
|
||||
new Server("assuredly.not.fyi", Server.ConnectionType.SSL, 50002),
|
||||
new Server("ax101.blockeng.ch", Server.ConnectionType.SSL, 50002),
|
||||
new Server("ax102.blockeng.ch", Server.ConnectionType.SSL, 50002),
|
||||
new Server("b.1209k.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("b6.1209k.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("bitcoin.dermichi.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("bitcoin.lu.ke", Server.ConnectionType.SSL, 50002),
|
||||
new Server("bitcoin.lukechilds.co", Server.ConnectionType.SSL, 50002),
|
||||
new Server("blkhub.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("btc.lastingcoin.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("btc.electroncash.dk", Server.ConnectionType.SSL, 60002),
|
||||
new Server("btc.ocf.sh", Server.ConnectionType.SSL, 50002),
|
||||
new Server("btce.iiiiiii.biz", Server.ConnectionType.SSL, 50002),
|
||||
new Server("caleb.vegas", Server.ConnectionType.SSL, 50002),
|
||||
new Server("eai.coincited.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.bhoovd.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.bitaroo.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrumx.dev", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.bitcoinlizard.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.blockstream.info", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.emzy.de", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.exan.tech", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.kendigisland.xyz", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.mmitech.info", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.petrkr.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.stippy.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.thomasfischbach.de", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum0.snel.it", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum1.cipig.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum2.cipig.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum3.cipig.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrumx.alexridevski.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrumx-core.1209k.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("elx.bitske.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("ex03.axalgo.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("ex05.axalgo.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("ex07.axalgo.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("fortress.qtornado.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("fulcrum.grey.pw", Server.ConnectionType.SSL, 50002),
|
||||
new Server("fulcrum.sethforprivacy.com", Server.ConnectionType.SSL, 51002),
|
||||
new Server("guichet.centure.cc", Server.ConnectionType.SSL, 50002),
|
||||
new Server("kareoke.qoppa.org", Server.ConnectionType.SSL, 50002),
|
||||
new Server("hodlers.beer", Server.ConnectionType.SSL, 50002),
|
||||
new Server("kareoke.qoppa.org", Server.ConnectionType.SSL, 50002),
|
||||
new Server("kirsche.emzy.de", Server.ConnectionType.SSL, 50002),
|
||||
new Server("node1.btccuracao.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("osr1ex1.compumundohipermegared.one", Server.ConnectionType.SSL, 50002),
|
||||
new Server("smmalis37.ddns.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("ulrichard.ch", Server.ConnectionType.SSL, 50002),
|
||||
new Server("vmd104012.contaboserver.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("vmd104014.contaboserver.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("vmd63185.contaboserver.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("vmd71287.contaboserver.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("vmd84592.contaboserver.net", Server.ConnectionType.SSL, 50002),
|
||||
new Server("xtrum.com", Server.ConnectionType.SSL, 50002));
|
||||
}
|
||||
|
||||
|
@ -167,6 +167,16 @@ public abstract class Bitcoiny implements ForeignBlockchain {
|
||||
return blockTimestamps.get(5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns height from latest block.
|
||||
* <p>
|
||||
* @throws ForeignBlockchainException if error occurs
|
||||
*/
|
||||
public int getBlockchainHeight() throws ForeignBlockchainException {
|
||||
int height = this.blockchainProvider.getCurrentHeight();
|
||||
return height;
|
||||
}
|
||||
|
||||
/** Returns fee per transaction KB. To be overridden for testnet/regtest. */
|
||||
public Coin getFeePerKb() {
|
||||
return this.bitcoinjContext.getFeePerKb();
|
||||
|
@ -45,6 +45,9 @@ public class Digibyte extends Bitcoiny {
|
||||
return Arrays.asList(
|
||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=dgb
|
||||
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 55002),
|
||||
new Server("electrum-dgb.qortal.online", ConnectionType.SSL, 50002),
|
||||
new Server("electrum1-dgb.qortal.online", ConnectionType.SSL, 50002),
|
||||
new Server("electrum1.cipig.net", ConnectionType.SSL, 20059),
|
||||
new Server("electrum2.cipig.net", ConnectionType.SSL, 20059),
|
||||
new Server("electrum3.cipig.net", ConnectionType.SSL, 20059));
|
||||
|
@ -45,11 +45,13 @@ public class Dogecoin extends Bitcoiny {
|
||||
public Collection<Server> getServers() {
|
||||
return Arrays.asList(
|
||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=doge
|
||||
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 54002),
|
||||
new Server("electrum-doge.qortal.online", ConnectionType.SSL, 50002),
|
||||
new Server("electrum1-doge.qortal.online", ConnectionType.SSL, 50002),
|
||||
new Server("electrum1.cipig.net", ConnectionType.SSL, 20060),
|
||||
new Server("electrum2.cipig.net", ConnectionType.SSL, 20060),
|
||||
new Server("electrum3.cipig.net", ConnectionType.SSL, 20060),
|
||||
new Server("161.97.137.235", ConnectionType.SSL, 50002));
|
||||
// TODO: add more mainnet servers. It's too centralized.
|
||||
new Server("electrum3.cipig.net", ConnectionType.SSL, 20060));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -45,17 +45,20 @@ public class Litecoin extends Bitcoiny {
|
||||
return Arrays.asList(
|
||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=ltc
|
||||
//CLOSED new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
|
||||
//CLOSED new Server("electrum-ltc.someguy123.net", Server.ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022),
|
||||
//BEHIND new Server("62.171.169.176", Server.ConnectionType.SSL, 50002),
|
||||
//PHISHY new Server("electrum-ltc.bysh.me", Server.ConnectionType.SSL, 50002),
|
||||
new Server("backup.electrum-ltc.org", Server.ConnectionType.SSL, 443),
|
||||
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum.ltc.xurious.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
|
||||
new Server("electrum-ltc.qortal.online", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum1-ltc.qortal.online", Server.ConnectionType.SSL, 50002),
|
||||
new Server("electrum1.cipig.net", Server.ConnectionType.SSL, 20063),
|
||||
new Server("electrum2.cipig.net", Server.ConnectionType.SSL, 20063),
|
||||
new Server("electrum3.cipig.net", Server.ConnectionType.SSL, 20063),
|
||||
new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022),
|
||||
new Server("ltc.rentonrisk.com", Server.ConnectionType.SSL, 50002),
|
||||
new Server("62.171.169.176", Server.ConnectionType.SSL, 50002));
|
||||
new Server("ltc.rentonrisk.com", Server.ConnectionType.SSL, 50002));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -57,9 +57,9 @@ public class PirateChain extends Bitcoiny {
|
||||
public Collection<Server> getServers() {
|
||||
return Arrays.asList(
|
||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||
new Server("arrrlightd.qortal.online", ConnectionType.SSL, 443),
|
||||
new Server("arrrlightd1.qortal.online", ConnectionType.SSL, 443),
|
||||
new Server("arrrlightd2.qortal.online", ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr1.qortal.online", ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr2.qortal.online", ConnectionType.SSL, 443),
|
||||
new Server("wallet-arrr3.qortal.online", ConnectionType.SSL, 443),
|
||||
new Server("lightd.pirate.black", ConnectionType.SSL, 443));
|
||||
}
|
||||
|
||||
|
@ -45,13 +45,17 @@ public class Ravencoin extends Bitcoiny {
|
||||
return Arrays.asList(
|
||||
// Servers chosen on NO BASIS WHATSOEVER from various sources!
|
||||
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=rvn
|
||||
new Server("aethyn.com", ConnectionType.SSL, 50002),
|
||||
new Server("electrum2.rvn.rocks", ConnectionType.SSL, 50002),
|
||||
new Server("rvn-dashboard.com", ConnectionType.SSL, 50002),
|
||||
new Server("rvn4lyfe.com", ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("aethyn.com", ConnectionType.SSL, 50002),
|
||||
//CLOSED new Server("electrum2.rvn.rocks", ConnectionType.SSL, 50002),
|
||||
//BEHIND new Server("electrum3.rvn.rocks", ConnectionType.SSL, 50002),
|
||||
new Server("electrum.qortal.link", Server.ConnectionType.SSL, 56002),
|
||||
new Server("electrum-rvn.qortal.online", ConnectionType.SSL, 50002),
|
||||
new Server("electrum1-rvn.qortal.online", ConnectionType.SSL, 50002),
|
||||
new Server("electrum1.cipig.net", ConnectionType.SSL, 20051),
|
||||
new Server("electrum2.cipig.net", ConnectionType.SSL, 20051),
|
||||
new Server("electrum3.cipig.net", ConnectionType.SSL, 20051));
|
||||
new Server("electrum3.cipig.net", ConnectionType.SSL, 20051),
|
||||
new Server("rvn-dashboard.com", ConnectionType.SSL, 50002),
|
||||
new Server("rvn4lyfe.com", ConnectionType.SSL, 50002));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -17,17 +17,21 @@ public class ActiveChats {
|
||||
private Long timestamp;
|
||||
private String sender;
|
||||
private String senderName;
|
||||
private byte[] signature;
|
||||
private byte[] data;
|
||||
|
||||
protected GroupChat() {
|
||||
/* JAXB */
|
||||
}
|
||||
|
||||
public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName) {
|
||||
public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName, byte[] signature, byte[] data) {
|
||||
this.groupId = groupId;
|
||||
this.groupName = groupName;
|
||||
this.timestamp = timestamp;
|
||||
this.sender = sender;
|
||||
this.senderName = senderName;
|
||||
this.signature = signature;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getGroupId() {
|
||||
@ -49,6 +53,14 @@ public class ActiveChats {
|
||||
public String getSenderName() {
|
||||
return this.senderName;
|
||||
}
|
||||
|
||||
public byte[] getSignature() {
|
||||
return this.signature;
|
||||
}
|
||||
|
||||
public byte[] getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@ -118,4 +130,4 @@ public class ActiveChats {
|
||||
return this.direct;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -16,6 +16,8 @@ import org.qortal.repository.Repository;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
import org.qortal.utils.Unicode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Name {
|
||||
|
||||
// Properties
|
||||
@ -116,7 +118,7 @@ public class Name {
|
||||
|
||||
this.repository.getNameRepository().save(this.nameData);
|
||||
|
||||
if (!updateNameTransactionData.getNewName().isEmpty())
|
||||
if (!updateNameTransactionData.getNewName().isEmpty() && !Objects.equals(updateNameTransactionData.getName(), updateNameTransactionData.getNewName()))
|
||||
// Name has changed, delete old entry
|
||||
this.repository.getNameRepository().delete(updateNameTransactionData.getNewName());
|
||||
|
||||
|
@ -119,7 +119,7 @@ public interface ATRepository {
|
||||
* <p>
|
||||
* NOTE: performs implicit <tt>repository.saveChanges()</tt>.
|
||||
*/
|
||||
public void rebuildLatestAtStates() throws DataException;
|
||||
public void rebuildLatestAtStates(int maxHeight) throws DataException;
|
||||
|
||||
|
||||
/** Returns height of first trimmable AT state. */
|
||||
|
@ -279,7 +279,9 @@ public class Bootstrap {
|
||||
|
||||
LOGGER.info("Generating checksum file...");
|
||||
String checksum = Crypto.digestHexString(compressedOutputPath.toFile(), 1024*1024);
|
||||
LOGGER.info("checksum: {}", checksum);
|
||||
Path checksumPath = Paths.get(String.format("%s.sha256", compressedOutputPath.toString()));
|
||||
LOGGER.info("Writing checksum to path: {}", checksumPath);
|
||||
Files.writeString(checksumPath, checksum, StandardOpenOption.CREATE);
|
||||
|
||||
// Return the path to the compressed bootstrap file
|
||||
|
@ -603,7 +603,7 @@ public class HSQLDBATRepository implements ATRepository {
|
||||
|
||||
|
||||
@Override
|
||||
public void rebuildLatestAtStates() throws DataException {
|
||||
public void rebuildLatestAtStates(int maxHeight) throws DataException {
|
||||
// latestATStatesLock is to prevent concurrent updates on LatestATStates
|
||||
// that could result in one process using a partial or empty dataset
|
||||
// because it was in the process of being rebuilt by another thread
|
||||
@ -624,11 +624,12 @@ public class HSQLDBATRepository implements ATRepository {
|
||||
+ "CROSS JOIN LATERAL("
|
||||
+ "SELECT height FROM ATStates "
|
||||
+ "WHERE ATStates.AT_address = ATs.AT_address "
|
||||
+ "AND height <= ?"
|
||||
+ "ORDER BY AT_address DESC, height DESC LIMIT 1"
|
||||
+ ") "
|
||||
+ ")";
|
||||
try {
|
||||
this.repository.executeCheckedUpdate(insertSql);
|
||||
this.repository.executeCheckedUpdate(insertSql, maxHeight);
|
||||
} catch (SQLException e) {
|
||||
repository.examineException(e);
|
||||
throw new DataException("Unable to populate temporary latest AT states cache in repository", e);
|
||||
|
@ -177,11 +177,11 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
private List<GroupChat> getActiveGroupChats(String address) throws DataException {
|
||||
// Find groups where address is a member and potential latest message details
|
||||
String groupsSql = "SELECT group_id, group_name, latest_timestamp, sender, sender_name "
|
||||
String groupsSql = "SELECT group_id, group_name, latest_timestamp, sender, sender_name, signature, data "
|
||||
+ "FROM GroupMembers "
|
||||
+ "JOIN Groups USING (group_id) "
|
||||
+ "LEFT OUTER JOIN LATERAL("
|
||||
+ "SELECT created_when AS latest_timestamp, sender, name AS sender_name "
|
||||
+ "SELECT created_when AS latest_timestamp, sender, name AS sender_name, signature, data "
|
||||
+ "FROM ChatTransactions "
|
||||
+ "JOIN Transactions USING (signature) "
|
||||
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
||||
@ -205,8 +205,10 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
|
||||
String sender = resultSet.getString(4);
|
||||
String senderName = resultSet.getString(5);
|
||||
byte[] signature = resultSet.getBytes(6);
|
||||
byte[] data = resultSet.getBytes(7);
|
||||
|
||||
GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName);
|
||||
GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName, signature, data);
|
||||
groupChats.add(groupChat);
|
||||
} while (resultSet.next());
|
||||
}
|
||||
@ -215,7 +217,7 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
}
|
||||
|
||||
// We need different SQL to handle group-less chat
|
||||
String grouplessSql = "SELECT created_when, sender, SenderNames.name "
|
||||
String grouplessSql = "SELECT created_when, sender, SenderNames.name, signature, data "
|
||||
+ "FROM ChatTransactions "
|
||||
+ "JOIN Transactions USING (signature) "
|
||||
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
|
||||
@ -228,15 +230,19 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
Long timestamp = null;
|
||||
String sender = null;
|
||||
String senderName = null;
|
||||
byte[] signature = null;
|
||||
byte[] data = null;
|
||||
|
||||
if (resultSet != null) {
|
||||
// We found a recipient-less, group-less CHAT message, so report its details
|
||||
timestamp = resultSet.getLong(1);
|
||||
sender = resultSet.getString(2);
|
||||
senderName = resultSet.getString(3);
|
||||
signature = resultSet.getBytes(4);
|
||||
data = resultSet.getBytes(5);
|
||||
}
|
||||
|
||||
GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName);
|
||||
GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName, signature, data);
|
||||
groupChats.add(groupChat);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch active group chats from repository", e);
|
||||
@ -291,4 +297,4 @@ public class HSQLDBChatRepository implements ChatRepository {
|
||||
return directChats;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -99,7 +99,7 @@ public class HSQLDBDatabasePruning {
|
||||
|
||||
// It's essential that we rebuild the latest AT states here, as we are using this data in the next query.
|
||||
// Failing to do this will result in important AT states being deleted, rendering the database unusable.
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(endHeight);
|
||||
|
||||
|
||||
// Loop through all the LatestATStates and copy them to the new table
|
||||
|
@ -216,7 +216,7 @@ public class Settings {
|
||||
public long recoveryModeTimeout = 10 * 60 * 1000L;
|
||||
|
||||
/** Minimum peer version number required in order to sync with them */
|
||||
private String minPeerVersion = "3.8.2";
|
||||
private String minPeerVersion = "3.8.7";
|
||||
/** 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 */
|
||||
|
@ -5,6 +5,7 @@ import java.util.List;
|
||||
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
|
||||
import org.qortal.data.naming.NameData;
|
||||
import org.qortal.data.transaction.CancelSellNameTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
@ -81,7 +82,13 @@ public class CancelSellNameTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public void preProcess() throws DataException {
|
||||
// Nothing to do
|
||||
CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) transactionData;
|
||||
|
||||
// Rebuild this name in the Names table from the transaction history
|
||||
// This is necessary because in some rare cases names can be missing from the Names table after registration
|
||||
// but we have been unable to reproduce the issue and track down the root cause
|
||||
NamesDatabaseIntegrityCheck namesDatabaseIntegrityCheck = new NamesDatabaseIntegrityCheck();
|
||||
namesDatabaseIntegrityCheck.rebuildName(cancelSellNameTransactionData.getName(), this.repository);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
83
src/main/resources/i18n/ApiError_pl.properties
Normal file
83
src/main/resources/i18n/ApiError_pl.properties
Normal file
@ -0,0 +1,83 @@
|
||||
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
|
||||
# Keys are from api.ApiError enum
|
||||
|
||||
# "localeLang": "pl",
|
||||
|
||||
### Common ###
|
||||
JSON = nie udało się przetworzyć wiadomości JSON
|
||||
|
||||
INSUFFICIENT_BALANCE = niedostateczne środki
|
||||
|
||||
UNAUTHORIZED = nieautoryzowane połączenie API
|
||||
|
||||
REPOSITORY_ISSUE = błąd repozytorium
|
||||
|
||||
NON_PRODUCTION = to wywołanie API nie jest dozwolone dla systemów produkcyjnych
|
||||
|
||||
BLOCKCHAIN_NEEDS_SYNC = blockchain musi się najpierw zsynchronizować
|
||||
|
||||
NO_TIME_SYNC = zegar się jeszcze nie zsynchronizował
|
||||
|
||||
### Validation ###
|
||||
INVALID_SIGNATURE = nieprawidłowa sygnatura
|
||||
|
||||
INVALID_ADDRESS = nieprawidłowy adres
|
||||
|
||||
INVALID_PUBLIC_KEY = nieprawidłowy klucz publiczny
|
||||
|
||||
INVALID_DATA = nieprawidłowe dane
|
||||
|
||||
INVALID_NETWORK_ADDRESS = nieprawidłowy adres sieci
|
||||
|
||||
ADDRESS_UNKNOWN = nieznany adres konta
|
||||
|
||||
INVALID_CRITERIA = nieprawidłowe kryteria wyszukiwania
|
||||
|
||||
INVALID_REFERENCE = nieprawidłowe skierowanie
|
||||
|
||||
TRANSFORMATION_ERROR = nie udało się przekształcić JSON w transakcję
|
||||
|
||||
INVALID_PRIVATE_KEY = klucz prywatny jest niepoprawny
|
||||
|
||||
INVALID_HEIGHT = nieprawidłowa wysokość bloku
|
||||
|
||||
CANNOT_MINT = konto nie możne bić monet
|
||||
|
||||
### Blocks ###
|
||||
BLOCK_UNKNOWN = blok nieznany
|
||||
|
||||
### Transactions ###
|
||||
TRANSACTION_UNKNOWN = nieznana transakcja
|
||||
|
||||
PUBLIC_KEY_NOT_FOUND = nie znaleziono klucza publicznego
|
||||
|
||||
# this one is special in that caller expected to pass two additional strings, hence the two %s
|
||||
TRANSACTION_INVALID = transakcja nieważna: %s (%s)
|
||||
|
||||
### Naming ###
|
||||
NAME_UNKNOWN = nazwa nieznana
|
||||
|
||||
### Asset ###
|
||||
INVALID_ASSET_ID = nieprawidłowy identyfikator aktywy
|
||||
|
||||
INVALID_ORDER_ID = nieprawidłowy identyfikator zlecenia aktywy
|
||||
|
||||
ORDER_UNKNOWN = nieznany identyfikator zlecenia aktywy
|
||||
|
||||
### Groups ###
|
||||
GROUP_UNKNOWN = nieznana grupa
|
||||
|
||||
### Foreign Blockchain ###
|
||||
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = obcy blockchain lub problem z siecią ElectrumX
|
||||
|
||||
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = niewystarczające środki na obcym blockchainie
|
||||
|
||||
FOREIGN_BLOCKCHAIN_TOO_SOON = zbyt wczesne nadawanie transakcji na obcym blockchainie (okres karencji/średni czas bloku)
|
||||
|
||||
### Trade Portal ###
|
||||
ORDER_SIZE_TOO_SMALL = zbyt niska kwota zlecenia
|
||||
|
||||
### Data ###
|
||||
FILE_NOT_FOUND = plik nie został znaleziony
|
||||
|
||||
NO_REPLY = peer nie odpowiedział w wyznaczonym czasie
|
@ -16,7 +16,7 @@ NON_PRODUCTION = этот вызов API не разрешен для произ
|
||||
|
||||
BLOCKCHAIN_NEEDS_SYNC = блокчейн должен сначала синхронизироваться
|
||||
|
||||
NO_TIME_SYNC = пока нет синхронизации часов
|
||||
NO_TIME_SYNC = время не синхронизировано
|
||||
|
||||
### Validation ###
|
||||
INVALID_SIGNATURE = недействительная подпись
|
||||
@ -72,7 +72,7 @@ FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = проблема с внешним блокч
|
||||
|
||||
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = недостаточный баланс на внешнем блокчейне
|
||||
|
||||
FOREIGN_BLOCKCHAIN_TOO_SOON = слишком рано для трансляции транзакции во внений блокчей (время блокировки/среднее время блока)
|
||||
FOREIGN_BLOCKCHAIN_TOO_SOON = слишком рано для трансляции транзакции во внешний блокчей (время блокировки/среднее время блока)
|
||||
|
||||
### Trade Portal ###
|
||||
ORDER_SIZE_TOO_SMALL = слишком маленькая сумма ордера
|
||||
@ -80,4 +80,4 @@ ORDER_SIZE_TOO_SMALL = слишком маленькая сумма ордера
|
||||
### Data ###
|
||||
FILE_NOT_FOUND = файл не найден
|
||||
|
||||
NO_REPLY = узел не ответил данными
|
||||
NO_REPLY = нет ответа
|
||||
|
@ -5,11 +5,11 @@ APPLYING_UPDATE_AND_RESTARTING = Automatisches Update anwenden und neu starten
|
||||
|
||||
AUTO_UPDATE = Automatisches Update
|
||||
|
||||
BLOCK_HEIGHT = height
|
||||
BLOCK_HEIGHT = Blockhöhe
|
||||
|
||||
BLOCKS_REMAINING = blocks remaining
|
||||
|
||||
BUILD_VERSION = Build-Version
|
||||
BUILD_VERSION = Entwicklungs-Version
|
||||
|
||||
CHECK_TIME_ACCURACY = Prüfe Zeitgenauigkeit
|
||||
|
||||
@ -23,7 +23,7 @@ CREATING_BACKUP_OF_DB_FILES = Erstellen Backup von Datenbank Dateien …
|
||||
|
||||
DB_BACKUP = Datenbank Backup
|
||||
|
||||
DB_CHECKPOINT = Datenbank Kontrollpunkt
|
||||
DB_CHECKPOINT = Datenbank Check
|
||||
|
||||
DB_MAINTENANCE = Datenbank Instandhaltung
|
||||
|
||||
@ -31,18 +31,18 @@ EXIT = Verlassen
|
||||
|
||||
LITE_NODE = Lite node
|
||||
|
||||
MINTING_DISABLED = NOT minting
|
||||
MINTING_DISABLED = Kein minting
|
||||
|
||||
MINTING_ENABLED = \u2714 Minting
|
||||
MINTING_ENABLED = \u2714 Minting aktiviert
|
||||
|
||||
OPEN_UI = Öffne UI
|
||||
|
||||
PERFORMING_DB_CHECKPOINT = Speichern nicht übergebener Datenbank Änderungen …
|
||||
PERFORMING_DB_CHECKPOINT = Speichern von unbestätigten Datenbankänderungen...
|
||||
|
||||
PERFORMING_DB_MAINTENANCE = Planmäßige Wartung durchführen...
|
||||
|
||||
SYNCHRONIZE_CLOCK = Synchronisiere Uhr
|
||||
|
||||
SYNCHRONIZING_BLOCKCHAIN = Synchronisierung
|
||||
SYNCHRONIZING_BLOCKCHAIN = Synchronisierung der Blockchain
|
||||
|
||||
SYNCHRONIZING_CLOCK = Synchronisierung Uhr
|
||||
SYNCHRONIZING_CLOCK = Synchronisierung der Uhr
|
||||
|
46
src/main/resources/i18n/SysTray_pl.properties
Normal file
46
src/main/resources/i18n/SysTray_pl.properties
Normal file
@ -0,0 +1,46 @@
|
||||
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
|
||||
# SysTray pop-up menu
|
||||
|
||||
APPLYING_UPDATE_AND_RESTARTING = Zastosowanie automatycznej aktualizacji i ponowne uruchomienie...
|
||||
|
||||
AUTO_UPDATE = Automatyczna aktualizacja
|
||||
|
||||
BLOCK_HEIGHT = wysokość
|
||||
|
||||
BUILD_VERSION = Wersja kompilacji
|
||||
|
||||
CHECK_TIME_ACCURACY = Sprawdz dokładność czasu
|
||||
|
||||
CONNECTING = Łączenie
|
||||
|
||||
CONNECTION = połączenie
|
||||
|
||||
CONNECTIONS = połączenia
|
||||
|
||||
CREATING_BACKUP_OF_DB_FILES = Tworzenie kopii zapasowej plików bazy danych...
|
||||
|
||||
DB_BACKUP = Kopia zapasowa bazy danych
|
||||
|
||||
DB_CHECKPOINT = Punkt kontrolny bazy danych...
|
||||
|
||||
DB_MAINTENANCE = Konserwacja bazy danych
|
||||
|
||||
EXIT = Zakończ
|
||||
|
||||
LITE_NODE = Lite node
|
||||
|
||||
MINTING_DISABLED = Mennica zamknięta
|
||||
|
||||
MINTING_ENABLED = \u2714 Mennica aktywna
|
||||
|
||||
OPEN_UI = Otwórz interfejs użytkownika
|
||||
|
||||
PERFORMING_DB_CHECKPOINT = Zapisywanie niezaksięgowanych zmian w bazie danych...
|
||||
|
||||
PERFORMING_DB_MAINTENANCE = Performing scheduled maintenance...
|
||||
|
||||
SYNCHRONIZE_CLOCK = Synchronizuj zegar
|
||||
|
||||
SYNCHRONIZING_BLOCKCHAIN = Synchronizacja
|
||||
|
||||
SYNCHRONIZING_CLOCK = Synchronizacja zegara
|
@ -1,7 +1,7 @@
|
||||
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
|
||||
# SysTray pop-up menu
|
||||
|
||||
APPLYING_UPDATE_AND_RESTARTING = Применение автоматического обновления и перезапуска...
|
||||
APPLYING_UPDATE_AND_RESTARTING = Применение автоматического обновления и перезапуск...
|
||||
|
||||
AUTO_UPDATE = Автоматическое обновление
|
||||
|
||||
|
195
src/main/resources/i18n/TransactionValidity_de.properties
Normal file
195
src/main/resources/i18n/TransactionValidity_de.properties
Normal file
@ -0,0 +1,195 @@
|
||||
#
|
||||
|
||||
ACCOUNT_ALREADY_EXISTS = Account existiert bereits
|
||||
|
||||
ACCOUNT_CANNOT_REWARD_SHARE = Account kann keine Belohnung teilen
|
||||
|
||||
ADDRESS_ABOVE_RATE_LIMIT = address hat das angegebene Geschwindigkeitlimit erreicht
|
||||
|
||||
ADDRESS_BLOCKED = Addresse ist geblockt
|
||||
|
||||
ALREADY_GROUP_ADMIN = bereits Gruppen Admin
|
||||
|
||||
ALREADY_GROUP_MEMBER = bereits Gruppen Mitglied
|
||||
|
||||
ALREADY_VOTED_FOR_THAT_OPTION = bereits für diese Option gestimmt
|
||||
|
||||
ASSET_ALREADY_EXISTS = asset existiert bereits
|
||||
|
||||
ASSET_DOES_NOT_EXIST = asset nicht gefunden
|
||||
|
||||
ASSET_DOES_NOT_MATCH_AT = asset passt nicht mit AT's asset
|
||||
|
||||
ASSET_NOT_SPENDABLE = asset ist nicht ausgabefähig
|
||||
|
||||
AT_ALREADY_EXISTS = AT existiert bereits
|
||||
|
||||
AT_IS_FINISHED = AT ist fertig
|
||||
|
||||
AT_UNKNOWN = AT unbekannt
|
||||
|
||||
BAN_EXISTS = ban besteht bereits
|
||||
|
||||
BAN_UNKNOWN = ban unbekannt
|
||||
|
||||
BANNED_FROM_GROUP = von der gruppe gebannt
|
||||
|
||||
BUYER_ALREADY_OWNER = Käufer ist bereits Besitzer
|
||||
|
||||
CLOCK_NOT_SYNCED = Uhr nicht synchronisiert
|
||||
|
||||
DUPLICATE_MESSAGE = Adresse sendete doppelte Nachricht
|
||||
|
||||
DUPLICATE_OPTION = Duplizierungsmöglichkeit
|
||||
|
||||
GROUP_ALREADY_EXISTS = Gruppe besteht bereits
|
||||
|
||||
GROUP_APPROVAL_DECIDED = Gruppenfreigabe bereits beschlossen
|
||||
|
||||
GROUP_APPROVAL_NOT_REQUIRED = Gruppenfreigabe nicht erforderlich
|
||||
|
||||
GROUP_DOES_NOT_EXIST = Gruppe nicht vorhanden
|
||||
|
||||
GROUP_ID_MISMATCH = Gruppen-ID stimmt nicht überein
|
||||
|
||||
GROUP_OWNER_CANNOT_LEAVE = Gruppenbesitzer kann Gruppe nicht verlassen
|
||||
|
||||
HAVE_EQUALS_WANT = das bessesene-asset ist das selbe wie das gesuchte-asset
|
||||
|
||||
INCORRECT_NONCE = falsche PoW-Nonce
|
||||
|
||||
INSUFFICIENT_FEE = unzureichende Gebühr
|
||||
|
||||
INVALID_ADDRESS = ungültige Adresse
|
||||
|
||||
INVALID_AMOUNT = ungültiger Betrag
|
||||
|
||||
INVALID_ASSET_OWNER = Ungültiger Eigentümer
|
||||
|
||||
INVALID_AT_TRANSACTION = ungültige AT-Transaktion
|
||||
|
||||
INVALID_AT_TYPE_LENGTH = ungültige AT 'Typ' Länge
|
||||
|
||||
INVALID_BUT_OK = ungültig aber OK
|
||||
|
||||
INVALID_CREATION_BYTES = ungültige Erstellungs der bytes
|
||||
|
||||
INVALID_DATA_LENGTH = ungültige Datenlänge
|
||||
|
||||
INVALID_DESCRIPTION_LENGTH = ungültige Länge der Beschreibung
|
||||
|
||||
INVALID_GROUP_APPROVAL_THRESHOLD = ungültiger Schwellenwert für die Gruppenzulassung
|
||||
|
||||
INVALID_GROUP_BLOCK_DELAY = Ungültige Blockverzögerung der Gruppenfreigabe
|
||||
|
||||
INVALID_GROUP_ID = ungültige Gruppen-ID
|
||||
|
||||
INVALID_GROUP_OWNER = ungültiger Gruppenbesitzer
|
||||
|
||||
INVALID_LIFETIME = unzulässige Lebensdauer
|
||||
|
||||
INVALID_NAME_LENGTH = ungültige Namenslänge
|
||||
|
||||
INVALID_NAME_OWNER = ungültiger Besitzername
|
||||
|
||||
INVALID_OPTION_LENGTH = ungültige Länge der Optionen
|
||||
|
||||
INVALID_OPTIONS_COUNT = Anzahl ungültiger Optionen
|
||||
|
||||
INVALID_ORDER_CREATOR = ungültiger Auftragsersteller
|
||||
|
||||
INVALID_PAYMENTS_COUNT = Anzahl ungültiger Zahlungen
|
||||
|
||||
INVALID_PUBLIC_KEY = ungültiger öffentlicher Schlüssel
|
||||
|
||||
INVALID_QUANTITY = unzulässige Menge
|
||||
|
||||
INVALID_REFERENCE = ungültige Referenz
|
||||
|
||||
INVALID_RETURN = ungültige Rückgabe
|
||||
|
||||
INVALID_REWARD_SHARE_PERCENT = ungültig Prozent der Belohnunganteile
|
||||
|
||||
INVALID_SELLER = unzulässiger Verkäufer
|
||||
|
||||
INVALID_TAGS_LENGTH = ungültige 'tags'-Länge
|
||||
|
||||
INVALID_TIMESTAMP_SIGNATURE = Ungültige Zeitstempel-Signatur
|
||||
|
||||
INVALID_TX_GROUP_ID = Ungültige Transaktionsgruppen-ID
|
||||
|
||||
INVALID_VALUE_LENGTH = ungültige 'Wert'-Länge
|
||||
|
||||
INVITE_UNKNOWN = Gruppeneinladung unbekannt
|
||||
|
||||
JOIN_REQUEST_EXISTS = Gruppeneinladung existiert bereits
|
||||
|
||||
MAXIMUM_REWARD_SHARES = die maximale Anzahl von Reward-Shares für dieses Konto erreicht
|
||||
|
||||
MISSING_CREATOR = fehlender Ersteller
|
||||
|
||||
MULTIPLE_NAMES_FORBIDDEN = mehrere registrierte Namen pro Konto sind untersagt
|
||||
|
||||
NAME_ALREADY_FOR_SALE = Name bereits zum Verkauf
|
||||
|
||||
NAME_ALREADY_REGISTERED = Name bereits registriert
|
||||
|
||||
NAME_BLOCKED = Name geblockt
|
||||
|
||||
NAME_DOES_NOT_EXIST = Name nicht vorhanden
|
||||
|
||||
NAME_NOT_FOR_SALE = Name ist unverkäuflich
|
||||
|
||||
NAME_NOT_NORMALIZED = Name nicht in Unicode-'normalisierter' Form
|
||||
|
||||
NEGATIVE_AMOUNT = ungültiger/negativer Betrag
|
||||
|
||||
NEGATIVE_FEE = ungültige/negative Gebühr
|
||||
|
||||
NEGATIVE_PRICE = ungültiger/negativer Preis
|
||||
|
||||
NO_BALANCE = unzureichendes Guthaben
|
||||
|
||||
NO_BLOCKCHAIN_LOCK = die Blockchain des Knotens ist beschäftigt
|
||||
|
||||
NO_FLAG_PERMISSION = Konto hat diese Berechtigung nicht
|
||||
|
||||
NOT_GROUP_ADMIN = Account ist kein Gruppenadmin
|
||||
|
||||
NOT_GROUP_MEMBER = Account kein Gruppenmitglied
|
||||
|
||||
NOT_MINTING_ACCOUNT = Account kann nicht minten
|
||||
|
||||
NOT_YET_RELEASED = Funktion noch nicht freigegeben
|
||||
|
||||
OK = OK
|
||||
|
||||
ORDER_ALREADY_CLOSED = Asset Trade Order ist bereits geschlossen
|
||||
|
||||
ORDER_DOES_NOT_EXIST = asset trade order existiert nicht
|
||||
|
||||
POLL_ALREADY_EXISTS = Umfrage bereits vorhanden
|
||||
|
||||
POLL_DOES_NOT_EXIST = Umfrage nicht vorhanden
|
||||
|
||||
POLL_OPTION_DOES_NOT_EXIST = Umfrageoption existiert nicht
|
||||
|
||||
PUBLIC_KEY_UNKNOWN = öffentlicher Schlüssel unbekannt
|
||||
|
||||
REWARD_SHARE_UNKNOWN = Geteilte Belohnungen unbekant
|
||||
|
||||
SELF_SHARE_EXISTS = Selbstbeteiligung (Geteilte Belohnungen) sind breits vorhanden
|
||||
|
||||
TIMESTAMP_TOO_NEW = Zeitstempel zu neu
|
||||
|
||||
TIMESTAMP_TOO_OLD = Zeitstempel zu alt
|
||||
|
||||
TOO_MANY_UNCONFIRMED = Account hat zu viele unbestätigte Transaktionen am laufen
|
||||
|
||||
TRANSACTION_ALREADY_CONFIRMED = Transaktionen sind bereits bestätigt
|
||||
|
||||
TRANSACTION_ALREADY_EXISTS = Transaktionen existiert bereits
|
||||
|
||||
TRANSACTION_UNKNOWN = Unbekante Transaktion
|
||||
|
||||
TX_GROUP_ID_MISMATCH = Transaktion Gruppen ID stimmt nicht überein
|
196
src/main/resources/i18n/TransactionValidity_pl.properties
Normal file
196
src/main/resources/i18n/TransactionValidity_pl.properties
Normal file
@ -0,0 +1,196 @@
|
||||
#
|
||||
|
||||
ACCOUNT_ALREADY_EXISTS = konto już istnieje
|
||||
|
||||
ACCOUNT_CANNOT_REWARD_SHARE = konto nie może udostępniać nagród
|
||||
|
||||
ADDRESS_ABOVE_RATE_LIMIT = adres osiągnął określony limit stawki
|
||||
|
||||
ADDRESS_BLOCKED = ten adres jest zablokowany
|
||||
|
||||
ALREADY_GROUP_ADMIN = już adminem grupy
|
||||
|
||||
ALREADY_GROUP_MEMBER = już członkiem grupy
|
||||
|
||||
ALREADY_VOTED_FOR_THAT_OPTION = już zagłosowano na ta opcje
|
||||
|
||||
ASSET_ALREADY_EXISTS = aktywa już istnieje
|
||||
|
||||
ASSET_DOES_NOT_EXIST = aktywa nie istnieje
|
||||
|
||||
ASSET_DOES_NOT_MATCH_AT = aktywa nie pasuje do aktywy AT
|
||||
|
||||
ASSET_NOT_SPENDABLE = aktywa nie jest rozporządzalna
|
||||
|
||||
AT_ALREADY_EXISTS = AT już istnieje
|
||||
|
||||
AT_IS_FINISHED = AT zakończył
|
||||
|
||||
AT_UNKNOWN = AT nieznany
|
||||
|
||||
BAN_EXISTS = ban już istnieje
|
||||
|
||||
BAN_UNKNOWN = ban nieznany
|
||||
|
||||
BANNED_FROM_GROUP = zbanowany z grupy
|
||||
|
||||
BUYER_ALREADY_OWNER = kupca jest już właścicielem
|
||||
|
||||
CLOCK_NOT_SYNCED = zegar nie zsynchronizowany
|
||||
|
||||
DUPLICATE_MESSAGE = adres wysłał duplikat wiadomości
|
||||
|
||||
DUPLICATE_OPTION = duplikat opcji
|
||||
|
||||
GROUP_ALREADY_EXISTS = grupa już istnieje
|
||||
|
||||
GROUP_APPROVAL_DECIDED = zatwierdzenie grupy już zdecydowano
|
||||
|
||||
GROUP_APPROVAL_NOT_REQUIRED = zatwierdzenie grupy nie jest wymagane
|
||||
|
||||
GROUP_DOES_NOT_EXIST = grupa nie istnieje
|
||||
|
||||
GROUP_ID_MISMATCH = niedopasowanie identyfikatora grupy
|
||||
|
||||
GROUP_OWNER_CANNOT_LEAVE = właściciel grupy nie może opuścić grupy
|
||||
|
||||
HAVE_EQUALS_WANT = posiadana aktywa równa się chcianej aktywie
|
||||
|
||||
INCORRECT_NONCE = nieprawidłowy nonce PoW
|
||||
|
||||
INSUFFICIENT_FEE = niewystarczająca opłata
|
||||
|
||||
INVALID_ADDRESS = nieprawidłowy adres
|
||||
|
||||
INVALID_AMOUNT = nieprawidłowa kwota
|
||||
|
||||
INVALID_ASSET_OWNER = nieprawidłowy właściciel aktywów
|
||||
|
||||
INVALID_AT_TRANSACTION = nieważna transakcja AT
|
||||
|
||||
INVALID_AT_TYPE_LENGTH = nieprawidłowa długość typu AT
|
||||
|
||||
INVALID_BUT_OK = nieważne, ale OK
|
||||
|
||||
INVALID_CREATION_BYTES = nieprawidłowe bajty tworzenia
|
||||
|
||||
INVALID_DATA_LENGTH = nieprawidłowa długość danych
|
||||
|
||||
INVALID_DESCRIPTION_LENGTH = nieprawidłowa długość opisu
|
||||
|
||||
INVALID_GROUP_APPROVAL_THRESHOLD = nieprawidłowy próg zatwierdzenia grupy
|
||||
|
||||
INVALID_GROUP_BLOCK_DELAY = nieprawidłowe opóźnienie bloku zatwierdzenia grupy
|
||||
|
||||
INVALID_GROUP_ID = nieprawidłowy identyfikator grupy
|
||||
|
||||
INVALID_GROUP_OWNER = nieprawidłowy właściciel grupy
|
||||
|
||||
INVALID_LIFETIME = nieprawidłowy czas istnienia
|
||||
|
||||
INVALID_NAME_LENGTH = nieprawidłowa długość nazwy
|
||||
|
||||
INVALID_NAME_OWNER = nieprawidłowy właściciel nazwy
|
||||
|
||||
INVALID_OPTION_LENGTH = nieprawidłowa długość opcji
|
||||
|
||||
INVALID_OPTIONS_COUNT = nieprawidłowa liczba opcji
|
||||
|
||||
INVALID_ORDER_CREATOR = nieprawidłowy twórca zlecenia
|
||||
|
||||
INVALID_PAYMENTS_COUNT = nieprawidłowa liczba płatności
|
||||
|
||||
INVALID_PUBLIC_KEY = nieprawidłowy klucz publiczny
|
||||
|
||||
INVALID_QUANTITY = nieprawidłowa ilość
|
||||
|
||||
INVALID_REFERENCE = nieprawidłowe skierowanie
|
||||
|
||||
INVALID_RETURN = nieprawidłowy zwrot
|
||||
|
||||
INVALID_REWARD_SHARE_PERCENT = nieprawidłowy procent udziału w nagrodzie
|
||||
|
||||
INVALID_SELLER = nieprawidłowy sprzedawca
|
||||
|
||||
INVALID_TAGS_LENGTH = nieprawidłowa długość tagów
|
||||
|
||||
INVALID_TIMESTAMP_SIGNATURE = nieprawidłowa sygnatura znacznika czasu
|
||||
|
||||
INVALID_TX_GROUP_ID = nieprawidłowy identyfikator grupy transakcji
|
||||
|
||||
INVALID_VALUE_LENGTH = nieprawidłowa długość wartości
|
||||
|
||||
INVITE_UNKNOWN = zaproszenie do grupy nieznane
|
||||
|
||||
JOIN_REQUEST_EXISTS = wniosek o dołączenie do grupy już istnieje
|
||||
|
||||
MAXIMUM_REWARD_SHARES = osiągnięto już maksymalną liczbę udziałów w nagrodzie dla tego konta
|
||||
|
||||
MISSING_CREATOR = brak twórcy
|
||||
|
||||
MULTIPLE_NAMES_FORBIDDEN = zabronione jest używanie wielu nazw na jednym koncie
|
||||
|
||||
NAME_ALREADY_FOR_SALE = nazwa już wystawiona na sprzedaż
|
||||
|
||||
NAME_ALREADY_REGISTERED = nazwa już zarejestrowana
|
||||
|
||||
NAME_BLOCKED = ta nazwa jest zablokowana
|
||||
|
||||
NAME_DOES_NOT_EXIST = nazwa nie istnieje
|
||||
|
||||
NAME_NOT_FOR_SALE = nazwa nie jest przeznaczona do sprzedaży
|
||||
|
||||
NAME_NOT_NORMALIZED = nazwa nie jest w formie 'znormalizowanej' Unicode
|
||||
|
||||
NEGATIVE_AMOUNT = nieprawidłowa/ujemna kwota
|
||||
|
||||
NEGATIVE_FEE = nieprawidłowa/ujemna opłata
|
||||
|
||||
NEGATIVE_PRICE = nieprawidłowa/ujemna cena
|
||||
|
||||
NO_BALANCE = niewystarczające środki
|
||||
|
||||
NO_BLOCKCHAIN_LOCK = węzeł blockchain jest obecnie zajęty
|
||||
|
||||
NO_FLAG_PERMISSION = konto nie ma tego uprawnienia
|
||||
|
||||
NOT_GROUP_ADMIN = konto nie jest adminem grupy
|
||||
|
||||
NOT_GROUP_MEMBER = konto nie jest członkiem grupy
|
||||
|
||||
NOT_MINTING_ACCOUNT = konto nie może bić monet
|
||||
|
||||
NOT_YET_RELEASED = funkcja nie została jeszcze udostępniona
|
||||
|
||||
OK = OK
|
||||
|
||||
ORDER_ALREADY_CLOSED = zlecenie handlu aktywami jest już zakończone
|
||||
|
||||
ORDER_DOES_NOT_EXIST = zlecenie sprzedaży aktywów nie istnieje
|
||||
|
||||
POLL_ALREADY_EXISTS = ankieta już istnieje
|
||||
|
||||
POLL_DOES_NOT_EXIST = ankieta nie istnieje
|
||||
|
||||
POLL_OPTION_DOES_NOT_EXIST = opcja ankiety nie istnieje
|
||||
|
||||
PUBLIC_KEY_UNKNOWN = klucz publiczny nieznany
|
||||
|
||||
REWARD_SHARE_UNKNOWN = nieznany udział w nagrodzie
|
||||
|
||||
SELF_SHARE_EXISTS = samoudział (udział w nagrodzie) już istnieje
|
||||
|
||||
TIMESTAMP_TOO_NEW = zbyt nowy znacznik czasu
|
||||
|
||||
TIMESTAMP_TOO_OLD = zbyt stary znacznik czasu
|
||||
|
||||
TOO_MANY_UNCONFIRMED = rachunek ma zbyt wiele niepotwierdzonych transakcji w toku
|
||||
|
||||
TRANSACTION_ALREADY_CONFIRMED = transakcja została już potwierdzona
|
||||
|
||||
TRANSACTION_ALREADY_EXISTS = transakcja już istnieje
|
||||
|
||||
TRANSACTION_UNKNOWN = transakcja nieznana
|
||||
|
||||
TX_GROUP_ID_MISMATCH = niezgodność ID grupy transakcji
|
||||
|
@ -23,7 +23,6 @@ import org.qortal.transform.TransformationException;
|
||||
import org.qortal.transform.block.BlockTransformation;
|
||||
import org.qortal.utils.BlockArchiveUtils;
|
||||
import org.qortal.utils.NTP;
|
||||
import org.qortal.utils.Triple;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@ -314,9 +313,10 @@ public class BlockArchiveTests extends Common {
|
||||
repository.getBlockRepository().setBlockPruneHeight(901);
|
||||
|
||||
// Prune the AT states for the archived blocks
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(900);
|
||||
repository.saveChanges();
|
||||
int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900);
|
||||
assertEquals(900-1, numATStatesPruned);
|
||||
assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state
|
||||
repository.getATRepository().setAtPruneHeight(901);
|
||||
|
||||
// Now ensure the SQL repository is missing blocks 2 and 900...
|
||||
@ -563,16 +563,23 @@ public class BlockArchiveTests extends Common {
|
||||
// Trim the first 500 blocks
|
||||
repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500);
|
||||
repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501);
|
||||
repository.getATRepository().rebuildLatestAtStates(500);
|
||||
repository.getATRepository().trimAtStates(0, 500, 1000);
|
||||
repository.getATRepository().setAtTrimHeight(501);
|
||||
|
||||
// Now block 500 should only have the AT state data hash
|
||||
block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500);
|
||||
atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500);
|
||||
// Now block 499 should only have the AT state data hash
|
||||
List<ATStateData> block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499);
|
||||
atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499);
|
||||
assertNotNull(atStatesData.getStateHash());
|
||||
assertNull(atStatesData.getStateData());
|
||||
|
||||
// ... but block 501 should have the full data
|
||||
// ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range
|
||||
block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500);
|
||||
atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500);
|
||||
assertNotNull(atStatesData.getStateHash());
|
||||
assertNotNull(atStatesData.getStateData());
|
||||
|
||||
// ... and block 501 should also have the full data
|
||||
List<ATStateData> block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501);
|
||||
atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501);
|
||||
assertNotNull(atStatesData.getStateHash());
|
||||
@ -612,9 +619,10 @@ public class BlockArchiveTests extends Common {
|
||||
repository.getBlockRepository().setBlockPruneHeight(501);
|
||||
|
||||
// Prune the AT states for the archived blocks
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(500);
|
||||
repository.saveChanges();
|
||||
int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500);
|
||||
assertEquals(499, numATStatesPruned);
|
||||
assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state
|
||||
repository.getATRepository().setAtPruneHeight(501);
|
||||
|
||||
// Now ensure the SQL repository is missing blocks 2 and 500...
|
||||
|
@ -176,7 +176,8 @@ public class BootstrapTests extends Common {
|
||||
repository.getBlockRepository().setBlockPruneHeight(901);
|
||||
|
||||
// Prune the AT states for the archived blocks
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(900);
|
||||
repository.saveChanges();
|
||||
repository.getATRepository().pruneAtStates(0, 900);
|
||||
repository.getATRepository().setAtPruneHeight(901);
|
||||
|
||||
|
@ -1,16 +1,33 @@
|
||||
package org.qortal.test;
|
||||
|
||||
import com.google.common.hash.HashCode;
|
||||
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.crosschain.AcctMode;
|
||||
import org.qortal.crosschain.LitecoinACCTv3;
|
||||
import org.qortal.data.at.ATData;
|
||||
import org.qortal.data.at.ATStateData;
|
||||
import org.qortal.data.block.BlockData;
|
||||
import org.qortal.data.crosschain.CrossChainTradeData;
|
||||
import org.qortal.data.transaction.BaseTransactionData;
|
||||
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||
import org.qortal.data.transaction.MessageTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.group.Group;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.test.common.AtUtils;
|
||||
import org.qortal.test.common.BlockUtils;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.transaction.DeployAtTransaction;
|
||||
import org.qortal.transaction.MessageTransaction;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -19,6 +36,13 @@ import static org.junit.Assert.*;
|
||||
|
||||
public class PruneTests extends Common {
|
||||
|
||||
// Constants for test AT (an LTC ACCT)
|
||||
public static final byte[] litecoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
|
||||
public static final int tradeTimeout = 20; // blocks
|
||||
public static final long redeemAmount = 80_40200000L;
|
||||
public static final long fundingAmount = 123_45600000L;
|
||||
public static final long litecoinAmount = 864200L; // 0.00864200 LTC
|
||||
|
||||
@Before
|
||||
public void beforeTest() throws DataException {
|
||||
Common.useDefaultSettings();
|
||||
@ -62,23 +86,32 @@ public class PruneTests extends Common {
|
||||
repository.getBlockRepository().setBlockPruneHeight(6);
|
||||
|
||||
// Prune AT states for blocks 2-5
|
||||
repository.getATRepository().rebuildLatestAtStates(5);
|
||||
repository.saveChanges();
|
||||
int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 5);
|
||||
assertEquals(4, numATStatesPruned);
|
||||
assertEquals(3, numATStatesPruned);
|
||||
repository.getATRepository().setAtPruneHeight(6);
|
||||
|
||||
// Make sure that blocks 2-5 are now missing block data and AT states data
|
||||
for (Integer i=2; i <= 5; i++) {
|
||||
// Make sure that blocks 2-4 are now missing block data and AT states data
|
||||
for (Integer i=2; i <= 4; i++) {
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(i);
|
||||
assertNull(blockData);
|
||||
List<ATStateData> atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(i);
|
||||
assertTrue(atStatesDataList.isEmpty());
|
||||
}
|
||||
|
||||
// ... but blocks 6-10 have block data and full AT states data
|
||||
// Block 5 should have full AT states data even though it was pruned.
|
||||
// This is because we identified that as the "latest" AT state in that block range
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(5);
|
||||
assertNull(blockData);
|
||||
List<ATStateData> atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(5);
|
||||
assertEquals(1, atStatesDataList.size());
|
||||
|
||||
// Blocks 6-10 have block data and full AT states data
|
||||
for (Integer i=6; i <= 10; i++) {
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(i);
|
||||
blockData = repository.getBlockRepository().fromHeight(i);
|
||||
assertNotNull(blockData.getSignature());
|
||||
List<ATStateData> atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(i);
|
||||
atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(i);
|
||||
assertNotNull(atStatesDataList);
|
||||
assertFalse(atStatesDataList.isEmpty());
|
||||
ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(atStatesDataList.get(0).getATAddress(), i);
|
||||
@ -88,4 +121,102 @@ public class PruneTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPruneSleepingAt() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
|
||||
PrivateKeyAccount tradeAccount = Common.getTestAccount(repository, "alice");
|
||||
|
||||
DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
|
||||
Account at = deployAtTransaction.getATAccount();
|
||||
String atAddress = at.getAddress();
|
||||
|
||||
// Mint enough blocks to take the original DEPLOY_AT past the prune threshold (in this case 20)
|
||||
Block block = BlockUtils.mintBlocks(repository, 25);
|
||||
|
||||
// Send creator's address to AT, instead of typical partner's address
|
||||
byte[] messageData = LitecoinACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
|
||||
long txTimestamp = block.getBlockData().getTimestamp();
|
||||
MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress, txTimestamp);
|
||||
|
||||
// AT should process 'cancel' message in next block
|
||||
BlockUtils.mintBlock(repository);
|
||||
|
||||
// Prune AT states up to block 20
|
||||
repository.getATRepository().rebuildLatestAtStates(20);
|
||||
repository.saveChanges();
|
||||
int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 20);
|
||||
assertEquals(1, numATStatesPruned); // deleted state at heights 2, but state at height 3 remains
|
||||
|
||||
// Check AT is finished
|
||||
ATData atData = repository.getATRepository().fromATAddress(atAddress);
|
||||
assertTrue(atData.getIsFinished());
|
||||
|
||||
// AT should be in CANCELLED mode
|
||||
CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
|
||||
assertEquals(AcctMode.CANCELLED, tradeData.mode);
|
||||
|
||||
// Test orphaning - should be possible because the previous AT state at height 3 is still available
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Helper methods for AT testing
|
||||
private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
|
||||
byte[] creationBytes = LitecoinACCTv3.buildQortalAT(tradeAddress, litecoinPublicKeyHash, redeemAmount, litecoinAmount, tradeTimeout);
|
||||
|
||||
long txTimestamp = System.currentTimeMillis();
|
||||
byte[] lastReference = deployer.getLastReference();
|
||||
|
||||
if (lastReference == null) {
|
||||
System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
Long fee = null;
|
||||
String name = "QORT-LTC cross-chain trade";
|
||||
String description = String.format("Qortal-Litecoin cross-chain trade");
|
||||
String atType = "ACCT";
|
||||
String tags = "QORT-LTC ACCT";
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
|
||||
TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
|
||||
|
||||
DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
|
||||
|
||||
fee = deployAtTransaction.calcRecommendedFee();
|
||||
deployAtTransactionData.setFee(fee);
|
||||
|
||||
TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
|
||||
|
||||
return deployAtTransaction;
|
||||
}
|
||||
|
||||
private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient, long txTimestamp) throws DataException {
|
||||
byte[] lastReference = sender.getLastReference();
|
||||
|
||||
if (lastReference == null) {
|
||||
System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
Long fee = null;
|
||||
int version = 4;
|
||||
int nonce = 0;
|
||||
long amount = 0;
|
||||
Long assetId = null; // because amount is zero
|
||||
|
||||
BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
|
||||
TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
|
||||
|
||||
MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
|
||||
|
||||
fee = messageTransaction.calcRecommendedFee();
|
||||
messageTransactionData.setFee(fee);
|
||||
|
||||
TransactionUtils.signAndMint(repository, messageTransactionData, sender);
|
||||
|
||||
return messageTransaction;
|
||||
}
|
||||
}
|
||||
|
@ -2,29 +2,20 @@ package org.qortal.test.at;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import org.ciyam.at.CompilationException;
|
||||
import org.ciyam.at.MachineState;
|
||||
import org.ciyam.at.OpCode;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.data.at.ATData;
|
||||
import org.qortal.data.at.ATStateData;
|
||||
import org.qortal.data.transaction.BaseTransactionData;
|
||||
import org.qortal.data.transaction.DeployAtTransactionData;
|
||||
import org.qortal.data.transaction.TransactionData;
|
||||
import org.qortal.group.Group;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.test.common.AtUtils;
|
||||
import org.qortal.test.common.BlockUtils;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.transaction.DeployAtTransaction;
|
||||
|
||||
public class AtRepositoryTests extends Common {
|
||||
@ -76,7 +67,7 @@ public class AtRepositoryTests extends Common {
|
||||
Integer testHeight = maxHeight - 2;
|
||||
|
||||
// Trim AT state data
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(maxHeight);
|
||||
repository.getATRepository().trimAtStates(2, maxHeight, 1000);
|
||||
|
||||
ATStateData atStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight);
|
||||
@ -130,7 +121,7 @@ public class AtRepositoryTests extends Common {
|
||||
Integer testHeight = blockchainHeight;
|
||||
|
||||
// Trim AT state data
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(maxHeight);
|
||||
// COMMIT to check latest AT states persist / TEMPORARY table interaction
|
||||
repository.saveChanges();
|
||||
|
||||
@ -163,8 +154,8 @@ public class AtRepositoryTests extends Common {
|
||||
int maxTrimHeight = blockchainHeight - 4;
|
||||
Integer testHeight = maxTrimHeight + 1;
|
||||
|
||||
// Trim AT state data
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
// Trim AT state data (using a max height of maxTrimHeight + 1, so it is beyond the trimmed range)
|
||||
repository.getATRepository().rebuildLatestAtStates(maxTrimHeight + 1);
|
||||
repository.saveChanges();
|
||||
repository.getATRepository().trimAtStates(2, maxTrimHeight, 1000);
|
||||
|
||||
@ -333,7 +324,7 @@ public class AtRepositoryTests extends Common {
|
||||
Integer testHeight = maxHeight - 2;
|
||||
|
||||
// Trim AT state data
|
||||
repository.getATRepository().rebuildLatestAtStates();
|
||||
repository.getATRepository().rebuildLatestAtStates(maxHeight);
|
||||
repository.getATRepository().trimAtStates(2, maxHeight, 1000);
|
||||
|
||||
List<ATStateData> atStates = repository.getATRepository().getBlockATStatesAtHeight(testHeight);
|
||||
|
@ -20,6 +20,15 @@ public class BlockUtils {
|
||||
return BlockMinter.mintTestingBlock(repository, mintingAccount);
|
||||
}
|
||||
|
||||
/** Mints multiple blocks using "alice-reward-share" test account, and returns the final block. */
|
||||
public static Block mintBlocks(Repository repository, int count) throws DataException {
|
||||
Block block = null;
|
||||
for (int i=0; i<count; i++) {
|
||||
block = BlockUtils.mintBlock(repository);
|
||||
}
|
||||
return block;
|
||||
}
|
||||
|
||||
public static Long getNextBlockReward(Repository repository) throws DataException {
|
||||
int currentHeight = repository.getBlockRepository().getBlockchainHeight();
|
||||
|
||||
|
@ -219,6 +219,65 @@ public class UpdateTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
// Test that multiple UPDATE_NAME transactions work as expected, when using a matching name and newName string
|
||||
@Test
|
||||
public void testDoubleUpdateNameWithMatchingNewName() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
// Register-name
|
||||
PrivateKeyAccount alice = Common.getTestAccount(repository, "alice");
|
||||
String name = "name";
|
||||
String reducedName = "name";
|
||||
String data = "{\"age\":30}";
|
||||
|
||||
TransactionData initialTransactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), name, data);
|
||||
initialTransactionData.setFee(new RegisterNameTransaction(null, null).getUnitFee(initialTransactionData.getTimestamp()));
|
||||
TransactionUtils.signAndMint(repository, initialTransactionData, alice);
|
||||
|
||||
// Check name exists
|
||||
assertTrue(repository.getNameRepository().nameExists(name));
|
||||
assertNotNull(repository.getNameRepository().fromReducedName(reducedName));
|
||||
|
||||
// Update name
|
||||
TransactionData middleTransactionData = new UpdateNameTransactionData(TestTransaction.generateBase(alice), name, name, data);
|
||||
TransactionUtils.signAndMint(repository, middleTransactionData, alice);
|
||||
|
||||
// Check name still exists
|
||||
assertTrue(repository.getNameRepository().nameExists(name));
|
||||
assertNotNull(repository.getNameRepository().fromReducedName(reducedName));
|
||||
|
||||
// Update name again
|
||||
TransactionData newestTransactionData = new UpdateNameTransactionData(TestTransaction.generateBase(alice), name, name, data);
|
||||
TransactionUtils.signAndMint(repository, newestTransactionData, alice);
|
||||
|
||||
// Check name still exists
|
||||
assertTrue(repository.getNameRepository().nameExists(name));
|
||||
assertNotNull(repository.getNameRepository().fromReducedName(reducedName));
|
||||
|
||||
// Check updated timestamp is correct
|
||||
assertEquals((Long) newestTransactionData.getTimestamp(), repository.getNameRepository().fromName(name).getUpdated());
|
||||
|
||||
// orphan and recheck
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// Check name still exists
|
||||
assertTrue(repository.getNameRepository().nameExists(name));
|
||||
assertNotNull(repository.getNameRepository().fromReducedName(reducedName));
|
||||
|
||||
// Check updated timestamp is correct
|
||||
assertEquals((Long) middleTransactionData.getTimestamp(), repository.getNameRepository().fromName(name).getUpdated());
|
||||
|
||||
// orphan and recheck
|
||||
BlockUtils.orphanLastBlock(repository);
|
||||
|
||||
// Check name still exists
|
||||
assertTrue(repository.getNameRepository().nameExists(name));
|
||||
assertNotNull(repository.getNameRepository().fromReducedName(reducedName));
|
||||
|
||||
// Check updated timestamp is empty
|
||||
assertNull(repository.getNameRepository().fromName(name).getUpdated());
|
||||
}
|
||||
}
|
||||
|
||||
// Test that reverting using previous UPDATE_NAME works as expected
|
||||
@Test
|
||||
public void testIntermediateUpdateName() throws DataException {
|
||||
|
Loading…
Reference in New Issue
Block a user