Compare commits

...

33 Commits

Author SHA1 Message Date
CalDescent
e7a3e511bd Bump version to 3.8.5 2023-02-08 19:37:01 +00:00
CalDescent
6fca30ce75 Added GET /admin/summary/alltime endpoint, to view a summary of chain activity since genesis. 2023-02-07 19:56:54 +00:00
CalDescent
e903e59f7f Merge pull request #107 from QuickMythril/unused-address
Add unused foreign address to API calls
2023-02-06 18:43:22 +00:00
CalDescent
bef170df7e Updated PirateChain lightwallet servers. 2023-02-06 18:42:37 +00:00
QuickMythril
386bfa4e20 Merge pull request #108 from AlphaX-Projects/master
Add electrum servers and fix java reflect error
2023-02-05 07:38:30 -05:00
AlphaX-Projects
6f867031e2 Add electrum servers and fix java reflect error 2023-02-05 12:53:49 +01:00
QuickMythril
8f589391a6 Updated depreciated actions
Node.js 12 actions are deprecated. Please update the following actions to use Node.js 16: actions/checkout@v2, actions/cache@v2, actions/setup-java@v2. For more information see: https://github.blog/changelog/2022-09-22-github-actions-all-actions-will-begin-running-on-node16-instead-of-node12/
2023-02-04 21:57:31 -05:00
QuickMythril
30c9f63cb1 Add unused foreign address to API calls
POST ​/crosschain​/{COIN}/unusedaddress
2023-02-04 21:03:55 -05:00
QuickMythril
952b21d9bd Merge pull request #105 from QuickMythril/update-electrumx
Updated ElectrumX servers
2023-02-04 18:40:39 -05:00
QuickMythril
1f410a503e Updated ElectrumX servers 2023-02-04 18:30:31 -05:00
CalDescent
f5e30eeaf5 Merge pull request #104 from QuickMythril/foreign-height
Add foreign chain height to API calls
2023-02-01 20:28:55 +00:00
QuickMythril
21f5d9a3d0 Add foreign chain height to API calls
GET ​/crosschain​/{COIN}/height
2023-01-31 17:23:25 -05:00
CalDescent
ab34fae810 Merge pull request #90 from QuickMythril/german
Updated German translations
2023-01-28 20:22:11 +00:00
CalDescent
42f2d015b7 Merge branch 'master' into german 2023-01-28 20:22:02 +00:00
CalDescent
2181ece28d Merge pull request #89 from lexandr0s/patch-2
Update ApiError_ru.properties
2023-01-28 20:21:13 +00:00
CalDescent
03a5d0e5f9 Merge pull request #88 from lexandr0s/patch-1
Update SysTray_ru.properties
2023-01-28 20:21:00 +00:00
CalDescent
352f094272 Merge pull request #99 from Nuc1eoN/polish-translation
Add polish translation
2023-01-28 20:20:35 +00:00
CalDescent
c5c826453b Removed unnecessary join when finding MESSAGE transactions, which caused secret to be unavailable when querying pruned blocks. 2023-01-28 15:41:48 +00:00
CalDescent
e86b9b1caf Added additional Litecoin ElectrumX server. 2023-01-28 15:34:30 +00:00
CalDescent
7fc170575c Merge branch 'cancel-sell-name-fixes' 2023-01-28 12:11:42 +00:00
CalDescent
876658256f Prevent a P2SH address being funded for a trade if there is an unconfirmed buy or cancel request in progress for it already.
This prevents foreign coins from leaving the local wallet when there is a high probability that the trade will fail, and therefore should reduce the chances of losing transaction fees due to refunds.

Whenever this occurs, the UI will show "Trade has an existing buy request or is pending cancellation." after clicking Buy.
2023-01-28 11:57:15 +00:00
CalDescent
a24ba40d5c Added additional Dogecoin ElectrumX server. 2023-01-28 09:54:15 +00:00
CalDescent
06d8a21714 Added CANCEL_SELL_NAME equivalents to NamesDatabaseIntegrityCheck.java 2023-01-27 19:38:26 +00:00
CalDescent
ae44065d7e Fixed issue with CancelSellName transactions. 2023-01-27 19:34:23 +00:00
CalDescent
6ad0989ea2 Reduce log spam 2023-01-27 18:35:44 +00:00
CalDescent
5962ebd08a More logging improvements in ArbitraryDataReader.decrypt() 2023-01-27 16:56:53 +00:00
CalDescent
bf06d47842 Create an ArbitraryDataResource object when building. Eventually this could be passed in to the reader instead of the individual components (service, name, identifier, etc)
This is now used to improve logging when extracting.
2023-01-27 16:55:43 +00:00
CalDescent
8c708558cb Implemented ElectrumX version negotiation. Fixes issues with DOGE wallet. 2023-01-27 14:33:34 +00:00
CalDescent
6b36d94c6f Removed searchResultsTransactions cache, to simplify code. The hostedTransactions cache is still in place, which limits disk reads when searching, so this additional cache isn't really needed. 2023-01-27 12:48:42 +00:00
Nuc1eoN
8ddf4c9f9f Add polish translation 2022-10-09 15:35:19 +02:00
QuickMythril
ff40b8f8ab Updated German translations 2022-06-23 01:43:33 -04:00
lexandr0s
c03344caae Update ApiError_ru.properties 2022-06-04 23:57:25 +04:00
lexandr0s
237b39a524 Update SysTray_ru.properties 2022-06-04 23:50:03 +04:00
35 changed files with 1198 additions and 50 deletions

View File

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

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.qortal</groupId>
<artifactId>qortal</artifactId>
<version>3.8.4</version>
<version>3.8.5</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>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,6 +11,7 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
@@ -38,9 +39,12 @@ import org.qortal.crypto.Crypto;
import org.qortal.data.at.ATData;
import org.qortal.data.crosschain.CrossChainTradeData;
import org.qortal.data.crosschain.TradeBotData;
import org.qortal.data.transaction.MessageTransactionData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.transaction.Transaction;
import org.qortal.utils.Base58;
import org.qortal.utils.NTP;
@@ -223,6 +227,17 @@ public class CrossChainTradeBotResource {
if (crossChainTradeData.mode != AcctMode.OFFERING)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
// Check if there is a buy or a cancel request in progress for this trade
List<Transaction.TransactionType> txTypes = List.of(Transaction.TransactionType.MESSAGE);
List<TransactionData> unconfirmed = repository.getTransactionRepository().getUnconfirmedTransactions(txTypes, null, 0, 0, false);
for (TransactionData transactionData : unconfirmed) {
MessageTransactionData messageTransactionData = (MessageTransactionData) transactionData;
if (Objects.equals(messageTransactionData.getRecipient(), atAddress)) {
// There is a pending request for this trade, so block this buy attempt to reduce the risk of refunds
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Trade has an existing buy request or is pending cancellation.");
}
}
AcctTradeBot.ResponseResult result = TradeBot.getInstance().startResponse(repository, atData, acct, crossChainTradeData,
tradeBotRespondRequest.foreignKey, tradeBotRespondRequest.receivingAddress);

View File

@@ -60,6 +60,9 @@ public class ArbitraryDataReader {
private int layerCount;
private byte[] latestSignature;
// The resource being read
ArbitraryDataResource arbitraryDataResource = null;
public ArbitraryDataReader(String resourceId, ResourceIdType resourceIdType, Service service, String identifier) {
// Ensure names are always lowercase
if (resourceIdType == ResourceIdType.NAME) {
@@ -116,6 +119,11 @@ public class ArbitraryDataReader {
return new ArbitraryDataBuildQueueItem(this.resourceId, this.resourceIdType, this.service, this.identifier);
}
private ArbitraryDataResource createArbitraryDataResource() {
return new ArbitraryDataResource(this.resourceId, this.resourceIdType, this.service, this.identifier);
}
/**
* loadAsynchronously
*
@@ -163,6 +171,8 @@ public class ArbitraryDataReader {
return;
}
this.arbitraryDataResource = this.createArbitraryDataResource();
this.preExecute();
this.deleteExistingFiles();
this.fetch();
@@ -436,7 +446,7 @@ public class ArbitraryDataReader {
byte[] secret = this.secret58 != null ? Base58.decode(this.secret58) : null;
if (secret != null && secret.length == Transformer.AES256_LENGTH) {
try {
LOGGER.info("Decrypting using algorithm {}...", algorithm);
LOGGER.debug("Decrypting {} using algorithm {}...", this.arbitraryDataResource, algorithm);
Path unencryptedPath = Paths.get(this.workingPath.toString(), "zipped.zip");
SecretKey aesKey = new SecretKeySpec(secret, 0, secret.length, "AES");
AES.decryptFile(algorithm, aesKey, this.filePath.toString(), unencryptedPath.toString());
@@ -447,7 +457,7 @@ public class ArbitraryDataReader {
} catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException | NoSuchPaddingException
| BadPaddingException | IllegalBlockSizeException | IOException | InvalidKeyException e) {
LOGGER.info(String.format("Exception when decrypting using algorithm %s", algorithm), e);
LOGGER.info(String.format("Exception when decrypting %s using algorithm %s", this.arbitraryDataResource, algorithm), e);
throw new DataException(String.format("Unable to decrypt file at path %s using algorithm %s: %s", this.filePath, algorithm, e.getMessage()));
}
} else {

View File

@@ -48,7 +48,6 @@ public class ArbitraryDataStorageManager extends Thread {
private List<ArbitraryTransactionData> hostedTransactions;
private String searchQuery;
private List<ArbitraryTransactionData> searchResultsTransactions;
private static final long DIRECTORY_SIZE_CHECK_INTERVAL = 10 * 60 * 1000L; // 10 minutes
@@ -344,11 +343,6 @@ public class ArbitraryDataStorageManager extends Thread {
*/
public List<ArbitraryTransactionData> searchHostedTransactions(Repository repository, String query, Integer limit, Integer offset) {
// Load from results cache if we can (results that exists for the same query), to avoid disk reads
if (this.searchResultsTransactions != null && this.searchQuery.equals(query.toLowerCase())) {
return ArbitraryTransactionUtils.limitOffsetTransactions(this.searchResultsTransactions, limit, offset);
}
// Using cache if we can, to avoid disk reads
if (this.hostedTransactions == null) {
this.hostedTransactions = this.loadAllHostedTransactions(repository);
@@ -376,10 +370,7 @@ public class ArbitraryDataStorageManager extends Thread {
// Sort by newest first
searchResultsList.sort(Comparator.comparingLong(ArbitraryTransactionData::getTimestamp).reversed());
// Update cache
this.searchResultsTransactions = searchResultsList;
return ArbitraryTransactionUtils.limitOffsetTransactions(this.searchResultsTransactions, limit, offset);
return ArbitraryTransactionUtils.limitOffsetTransactions(searchResultsList, limit, offset);
}
/**

View File

@@ -102,6 +102,21 @@ public class NamesDatabaseIntegrityCheck {
}
}
// Process CANCEL_SELL_NAME transactions
if (currentTransaction.getType() == TransactionType.CANCEL_SELL_NAME) {
CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) currentTransaction;
Name nameObj = new Name(repository, cancelSellNameTransactionData.getName());
if (nameObj != null && nameObj.getNameData() != null) {
nameObj.cancelSell(cancelSellNameTransactionData);
modificationCount++;
LOGGER.trace("Processed CANCEL_SELL_NAME transaction for name {}", name);
}
else {
// Something went wrong
throw new DataException(String.format("Name data not found for name %s", cancelSellNameTransactionData.getName()));
}
}
// Process BUY_NAME transactions
if (currentTransaction.getType() == TransactionType.BUY_NAME) {
BuyNameTransactionData buyNameTransactionData = (BuyNameTransactionData) currentTransaction;
@@ -128,7 +143,7 @@ public class NamesDatabaseIntegrityCheck {
public int rebuildAllNames() {
int modificationCount = 0;
try (final Repository repository = RepositoryManager.getRepository()) {
List<String> names = this.fetchAllNames(repository);
List<String> names = this.fetchAllNames(repository); // TODO: de-duplicate, to speed up this process
for (String name : names) {
modificationCount += this.rebuildName(name, repository);
}
@@ -326,6 +341,10 @@ public class NamesDatabaseIntegrityCheck {
TransactionType.BUY_NAME, Arrays.asList("name = ?"), Arrays.asList(name));
signatures.addAll(buyNameTransactions);
List<byte[]> cancelSellNameTransactions = repository.getTransactionRepository().getSignaturesMatchingCustomCriteria(
TransactionType.CANCEL_SELL_NAME, Arrays.asList("name = ?"), Arrays.asList(name));
signatures.addAll(cancelSellNameTransactions);
List<TransactionData> transactions = new ArrayList<>();
for (byte[] signature : signatures) {
TransactionData transactionData = repository.getTransactionRepository().fromSignature(signature);
@@ -390,6 +409,12 @@ public class NamesDatabaseIntegrityCheck {
names.add(sellNameTransactionData.getName());
}
}
if ((transactionData instanceof CancelSellNameTransactionData)) {
CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) transactionData;
if (!names.contains(cancelSellNameTransactionData.getName())) {
names.add(cancelSellNameTransactionData.getName());
}
}
}
return names;
}

View File

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

View File

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

View File

@@ -45,6 +45,8 @@ 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-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));

View File

@@ -45,10 +45,12 @@ 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-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));
// TODO: add more mainnet servers. It's too centralized.
}
@Override

View File

@@ -5,6 +5,7 @@ import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.text.DecimalFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -30,7 +31,11 @@ public class ElectrumX extends BitcoinyBlockchainProvider {
private static final Logger LOGGER = LogManager.getLogger(ElectrumX.class);
private static final Random RANDOM = new Random();
// See: https://electrumx.readthedocs.io/en/latest/protocol-changes.html
private static final double MIN_PROTOCOL_VERSION = 1.2;
private static final double MAX_PROTOCOL_VERSION = 2.0; // Higher than current latest, for hopeful future-proofing
private static final String CLIENT_NAME = "Qortal";
private static final int BLOCK_HEADER_LENGTH = 80;
// "message": "daemon error: DaemonError({'code': -5, 'message': 'No such mempool or blockchain transaction. Use gettransaction for wallet transactions.'})"
@@ -679,6 +684,9 @@ public class ElectrumX extends BitcoinyBlockchainProvider {
this.scanner = new Scanner(this.socket.getInputStream());
this.scanner.useDelimiter("\n");
// All connections need to start with a version negotiation
this.connectedRpc("server.version");
// Check connection is suitable by asking for server features, including genesis block hash
JSONObject featuresJson = (JSONObject) this.connectedRpc("server.features");
@@ -725,6 +733,17 @@ public class ElectrumX extends BitcoinyBlockchainProvider {
JSONArray requestParams = new JSONArray();
requestParams.addAll(Arrays.asList(params));
// server.version needs additional params to negotiate a version
if (method.equals("server.version")) {
requestParams.add(CLIENT_NAME);
List<String> versions = new ArrayList<>();
DecimalFormat df = new DecimalFormat("#.#");
versions.add(df.format(MIN_PROTOCOL_VERSION));
versions.add(df.format(MAX_PROTOCOL_VERSION));
requestParams.add(versions);
}
requestJson.put("params", requestParams);
String request = requestJson.toJSONString() + "\n";

View File

@@ -45,15 +45,18 @@ 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.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));
}

View File

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

View File

@@ -45,13 +45,16 @@ 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-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

View File

@@ -3,6 +3,7 @@ package org.qortal.data.transaction;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlTransient;
import org.qortal.transaction.Transaction.TransactionType;
@@ -19,6 +20,11 @@ public class CancelSellNameTransactionData extends TransactionData {
@Schema(description = "which name to cancel selling", example = "my-name")
private String name;
// For internal use when orphaning
@XmlTransient
@Schema(hidden = true)
private Long salePrice;
// Constructors
// For JAXB
@@ -30,11 +36,17 @@ public class CancelSellNameTransactionData extends TransactionData {
this.creatorPublicKey = this.ownerPublicKey;
}
public CancelSellNameTransactionData(BaseTransactionData baseTransactionData, String name) {
public CancelSellNameTransactionData(BaseTransactionData baseTransactionData, String name, Long salePrice) {
super(TransactionType.CANCEL_SELL_NAME, baseTransactionData);
this.ownerPublicKey = baseTransactionData.creatorPublicKey;
this.name = name;
this.salePrice = salePrice;
}
/** From network/API */
public CancelSellNameTransactionData(BaseTransactionData baseTransactionData, String name) {
this(baseTransactionData, name, null);
}
// Getters / setters
@@ -47,4 +59,12 @@ public class CancelSellNameTransactionData extends TransactionData {
return this.name;
}
public Long getSalePrice() {
return this.salePrice;
}
public void setSalePrice(Long salePrice) {
this.salePrice = salePrice;
}
}

View File

@@ -180,8 +180,12 @@ public class Name {
}
public void cancelSell(CancelSellNameTransactionData cancelSellNameTransactionData) throws DataException {
// Mark not for-sale but leave price in case we want to orphan
// Update previous sale price in transaction data
cancelSellNameTransactionData.setSalePrice(this.nameData.getSalePrice());
// Mark not for-sale
this.nameData.setIsForSale(false);
this.nameData.setSalePrice(null);
// Save sale info into repository
this.repository.getNameRepository().save(this.nameData);
@@ -190,6 +194,7 @@ public class Name {
public void uncancelSell(CancelSellNameTransactionData cancelSellNameTransactionData) throws DataException {
// Mark as for-sale using existing price
this.nameData.setIsForSale(true);
this.nameData.setSalePrice(cancelSellNameTransactionData.getSalePrice());
// Save no-sale info into repository
this.repository.getNameRepository().save(this.nameData);

View File

@@ -339,7 +339,7 @@ public class Network {
try {
if (!isConnected) {
// Add this signature to the list of pending requests for this peer
LOGGER.info("Making connection to peer {} to request files for signature {}...", peerAddressString, Base58.encode(signature));
LOGGER.debug("Making connection to peer {} to request files for signature {}...", peerAddressString, Base58.encode(signature));
Peer peer = new Peer(peerData);
peer.setIsDataPeer(true);
peer.addPendingSignatureRequest(signature);

View File

@@ -988,6 +988,11 @@ public class HSQLDBDatabaseUpdates {
stmt.execute("CREATE INDEX ChatTransactionsChatReferenceIndex ON ChatTransactions (chat_reference)");
break;
case 46:
// We need to track the sale price when canceling a name sale, so it can be put back when orphaned
stmt.execute("ALTER TABLE CancelSellNameTransactions ADD sale_price QortalAmount");
break;
default:
// nothing to do
return false;

View File

@@ -28,7 +28,6 @@ public class HSQLDBMessageRepository implements MessageRepository {
StringBuilder sql = new StringBuilder(1024);
sql.append("SELECT signature from MessageTransactions "
+ "JOIN Transactions USING (signature) "
+ "JOIN BlockTransactions ON transaction_signature = signature "
+ "WHERE ");
List<String> whereClauses = new ArrayList<>();

View File

@@ -17,15 +17,16 @@ public class HSQLDBCancelSellNameTransactionRepository extends HSQLDBTransaction
}
TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException {
String sql = "SELECT name FROM CancelSellNameTransactions WHERE signature = ?";
String sql = "SELECT name, sale_price FROM CancelSellNameTransactions WHERE signature = ?";
try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) {
if (resultSet == null)
return null;
String name = resultSet.getString(1);
Long salePrice = resultSet.getLong(2);
return new CancelSellNameTransactionData(baseTransactionData, name);
return new CancelSellNameTransactionData(baseTransactionData, name, salePrice);
} catch (SQLException e) {
throw new DataException("Unable to fetch cancel sell name transaction from repository", e);
}
@@ -38,7 +39,7 @@ public class HSQLDBCancelSellNameTransactionRepository extends HSQLDBTransaction
HSQLDBSaver saveHelper = new HSQLDBSaver("CancelSellNameTransactions");
saveHelper.bind("signature", cancelSellNameTransactionData.getSignature()).bind("owner", cancelSellNameTransactionData.getOwnerPublicKey()).bind("name",
cancelSellNameTransactionData.getName());
cancelSellNameTransactionData.getName()).bind("sale_price", cancelSellNameTransactionData.getSalePrice());
try {
saveHelper.execute(this.repository);

View 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

View File

@@ -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 = нет ответа

View File

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

View 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

View File

@@ -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 = Автоматическое обновление

View 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

View 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

View File

@@ -165,6 +165,52 @@ public class BuySellTests extends Common {
assertEquals("price incorrect", price, nameData.getSalePrice());
}
@Test
public void testCancelSellNameAndRelist() throws DataException {
// Register-name and sell-name
testSellName();
// Cancel Sell-name
CancelSellNameTransactionData transactionData = new CancelSellNameTransactionData(TestTransaction.generateBase(alice), name);
TransactionUtils.signAndMint(repository, transactionData, alice);
NameData nameData;
// Check name is no longer for sale
nameData = repository.getNameRepository().fromName(name);
assertFalse(nameData.isForSale());
assertNull(nameData.getSalePrice());
// Re-sell-name
Long newPrice = random.nextInt(1000) * Amounts.MULTIPLIER;
SellNameTransactionData sellNameTransactionData = new SellNameTransactionData(TestTransaction.generateBase(alice), name, newPrice);
TransactionUtils.signAndMint(repository, sellNameTransactionData, alice);
// Check name is for sale
nameData = repository.getNameRepository().fromName(name);
assertTrue(nameData.isForSale());
assertEquals("price incorrect", newPrice, nameData.getSalePrice());
// Orphan sell-name
BlockUtils.orphanLastBlock(repository);
// Check name no longer for sale
nameData = repository.getNameRepository().fromName(name);
assertFalse(nameData.isForSale());
assertNull(nameData.getSalePrice());
// Orphan cancel-sell-name
BlockUtils.orphanLastBlock(repository);
// Check name is for sale (at original price)
nameData = repository.getNameRepository().fromName(name);
assertTrue(nameData.isForSale());
assertEquals("price incorrect", price, nameData.getSalePrice());
// Orphan sell-name and register-name
BlockUtils.orphanBlocks(repository, 2);
}
@Test
public void testBuyName() throws DataException {
// Register-name and sell-name