From 491e79b8e657625ecba93bdb0d13313ac925b56b Mon Sep 17 00:00:00 2001 From: catbref Date: Tue, 29 Oct 2019 17:46:55 +0000 Subject: [PATCH] ProxyForging->RewardShare massive refactor & more... Unified terminology from block "generator", "forger", etc. to "minter" Unified terminology "proxy forger" to "reward share" as it was incorrect, or at least highly ambiguous, which account had which role. AccountRepository.findRewardShares() has different arg order! Account.canMint() now returns true if account has 'founder' flag set. Added Account.canRewardShare() which returns whether acocunt can create a reward-share (e.g. level 5+ or founder). Fixed HSQLDBAssetRepository.getAllAssets() which had incorrect resultSet column indexes. Removed old traces of EnableForging transaction. ACCOUNT_LEVEL and ACCOUNT_FLAGS (genesis-block-only transaction types) now set target account's last-reference. This is allow later REWARD_SHARE transactions in genesis block and post-genesis transactions by that account. REWARD_SHARE transactions are now FREE, but only if minter is also recipient. If a self-reward-share already exists, then unless share-percent is zero (to terminate reward-share), any subsequent self-reward-share is invalid. Updated SysTray i18n properties file. BlockChain config file requires 'minAccountLevelToRewardShare' and optional 'minAccountLevelToMint'. Added potential, but currently unused, memory-hard PoW algorithm. Fixed/removed/disabled some unit tests. BlockMinter.generateTestingBlock asks Controller to pretend mintingAccount is 'online'. More testing needed! --- .../{ProxyKeys.java => RewardShareKeys.java} | 15 +- src/main/java/org/qora/account/Account.java | 24 +- .../org/qora/account/PrivateKeyAccount.java | 6 +- .../org/qora/api/model/ApiOnlineAccount.java | 16 +- .../qora/api/model/BlockForgerSummary.java | 37 -- .../qora/api/model/BlockMinterSummary.java | 42 +++ ...equest.java => RewardShareKeyRequest.java} | 8 +- .../qora/api/resource/AddressesResource.java | 66 ++-- .../org/qora/api/resource/AdminResource.java | 50 +-- .../org/qora/api/resource/BlocksResource.java | 22 +- src/main/java/org/qora/block/Block.java | 307 ++++++++--------- src/main/java/org/qora/block/BlockChain.java | 38 ++- .../{BlockGenerator.java => BlockMinter.java} | 105 +++--- .../java/org/qora/block/GenesisBlock.java | 26 +- .../java/org/qora/controller/Controller.java | 102 +++--- src/main/java/org/qora/crosschain/BTC.java | 4 +- .../java/org/qora/crypto/CiyamMemoryPoW.java | 83 +++++ .../org/qora/data/account/AccountData.java | 14 +- ...countData.java => MintingAccountData.java} | 30 +- .../qora/data/account/ProxyForgerData.java | 58 ---- .../qora/data/account/RewardShareData.java | 58 ++++ .../java/org/qora/data/block/BlockData.java | 36 +- .../org/qora/data/block/BlockSummaryData.java | 12 +- .../qora/data/network/PeerChainTipData.java | 12 +- .../EnableForgingTransactionData.java | 51 --- .../ProxyForgingTransactionData.java | 92 ----- .../RewardShareTransactionData.java | 92 +++++ .../data/transaction/TransactionData.java | 2 +- src/main/java/org/qora/mintsim.java | 182 ---------- src/main/java/org/qora/network/Network.java | 2 +- .../message/BlockSummariesMessage.java | 8 +- .../qora/network/message/HeightV2Message.java | 22 +- .../qora/repository/AccountRepository.java | 50 ++- .../org/qora/repository/BlockRepository.java | 14 +- .../hsqldb/HSQLDBAccountRepository.java | 202 ++++++----- .../hsqldb/HSQLDBAssetRepository.java | 6 +- .../hsqldb/HSQLDBBlockRepository.java | 80 +++-- .../hsqldb/HSQLDBDatabaseUpdates.java | 31 ++ ...LDBEnableForgingTransactionRepository.java | 50 --- ...QLDBProxyForgingTransactionRepository.java | 55 --- ...SQLDBRewardShareTransactionRepository.java | 55 +++ src/main/java/org/qora/settings/Settings.java | 2 +- .../transaction/AccountFlagsTransaction.java | 6 + .../transaction/AccountLevelTransaction.java | 6 + .../transaction/ProxyForgingTransaction.java | 193 ----------- .../transaction/RewardShareTransaction.java | 221 ++++++++++++ .../org/qora/transaction/Transaction.java | 12 +- .../transform/block/BlockTransformer.java | 50 +-- ...=> RewardShareTransactionTransformer.java} | 36 +- src/main/resources/blockchain.json | 138 ++++++-- src/main/resources/i18n/SysTray_en.properties | 4 +- src/main/resources/i18n/SysTray_zh.properties | 4 +- src/test/java/org/qora/test/BTCACCTTests.java | 8 +- src/test/java/org/qora/test/BlockTests.java | 6 +- .../java/org/qora/test/ChainWeightTests.java | 6 +- .../org/qora/test/CiyamMemoryPoWTests.java | 40 +++ .../org/qora/test/CompatibilityTests.java | 13 +- src/test/java/org/qora/test/CryptoTests.java | 2 +- src/test/java/org/qora/test/GuiTests.java | 6 + src/test/java/org/qora/test/OnlineTests.java | 316 ------------------ .../org/qora/test/api/AddressesApiTests.java | 14 +- .../java/org/qora/test/api/BlockApiTests.java | 12 +- .../org/qora/test/assets/OldTradingTests.java | 2 + .../org/qora/test/common/AccountUtils.java | 57 +--- .../java/org/qora/test/common/AssetUtils.java | 8 +- .../java/org/qora/test/common/Common.java | 18 +- .../java/org/qora/test/common/GroupUtils.java | 6 +- .../org/qora/test/common/TestAccount.java | 10 +- .../qora/test/common/TransactionUtils.java | 8 +- .../EnableForgingTestTransaction.java | 17 - ...n.java => RewardShareTestTransaction.java} | 10 +- .../qora/test/forging/ProxyForgingTests.java | 127 ------- .../qora/test/forging/RewardShareTests.java | 127 +++++++ .../org/qora/test/forging/RewardTests.java | 10 +- .../qora/test/group/GroupApprovalTests.java | 28 +- .../org/qora/test/naming/OrphaningTests.java | 30 +- src/test/resources/test-chain-old-asset.json | 61 ++-- src/test/resources/test-chain-v1.json | 52 +++ src/test/resources/test-chain-v2.json | 60 ++-- src/test/resources/test-settings-v1.json | 6 + 80 files changed, 1853 insertions(+), 2044 deletions(-) rename src/main/java/org/qora/{ProxyKeys.java => RewardShareKeys.java} (58%) delete mode 100644 src/main/java/org/qora/api/model/BlockForgerSummary.java create mode 100644 src/main/java/org/qora/api/model/BlockMinterSummary.java rename src/main/java/org/qora/api/model/{ProxyKeyRequest.java => RewardShareKeyRequest.java} (65%) rename src/main/java/org/qora/block/{BlockGenerator.java => BlockMinter.java} (75%) create mode 100644 src/main/java/org/qora/crypto/CiyamMemoryPoW.java rename src/main/java/org/qora/data/account/{ForgingAccountData.java => MintingAccountData.java} (60%) delete mode 100644 src/main/java/org/qora/data/account/ProxyForgerData.java create mode 100644 src/main/java/org/qora/data/account/RewardShareData.java delete mode 100644 src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java delete mode 100644 src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java create mode 100644 src/main/java/org/qora/data/transaction/RewardShareTransactionData.java delete mode 100644 src/main/java/org/qora/mintsim.java delete mode 100644 src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBEnableForgingTransactionRepository.java delete mode 100644 src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBProxyForgingTransactionRepository.java create mode 100644 src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java delete mode 100644 src/main/java/org/qora/transaction/ProxyForgingTransaction.java create mode 100644 src/main/java/org/qora/transaction/RewardShareTransaction.java rename src/main/java/org/qora/transform/transaction/{ProxyForgingTransactionTransformer.java => RewardShareTransactionTransformer.java} (62%) create mode 100644 src/test/java/org/qora/test/CiyamMemoryPoWTests.java delete mode 100644 src/test/java/org/qora/test/OnlineTests.java delete mode 100644 src/test/java/org/qora/test/common/transaction/EnableForgingTestTransaction.java rename src/test/java/org/qora/test/common/transaction/{ProxyForgingTestTransaction.java => RewardShareTestTransaction.java} (53%) delete mode 100644 src/test/java/org/qora/test/forging/ProxyForgingTests.java create mode 100644 src/test/java/org/qora/test/forging/RewardShareTests.java create mode 100644 src/test/resources/test-chain-v1.json create mode 100644 src/test/resources/test-settings-v1.json diff --git a/src/main/java/org/qora/ProxyKeys.java b/src/main/java/org/qora/RewardShareKeys.java similarity index 58% rename from src/main/java/org/qora/ProxyKeys.java rename to src/main/java/org/qora/RewardShareKeys.java index a27c3bc5..6a1afded 100644 --- a/src/main/java/org/qora/ProxyKeys.java +++ b/src/main/java/org/qora/RewardShareKeys.java @@ -8,11 +8,11 @@ import org.qora.account.PrivateKeyAccount; import org.qora.account.PublicKeyAccount; import org.qora.utils.Base58; -public class ProxyKeys { +public class RewardShareKeys { private static void usage() { - System.err.println("Usage: ProxyKeys "); - System.err.println("Example: ProxyKeys pYQ6DpQBJ2n72TCLJLScEvwhf3boxWy2kQEPynakwpj 6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb"); + System.err.println("Usage: RewardShareKeys "); + System.err.println("Example: RewardShareKeys pYQ6DpQBJ2n72TCLJLScEvwhf3boxWy2kQEPynakwpj 6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb"); System.exit(1); } @@ -26,15 +26,14 @@ public class ProxyKeys { PrivateKeyAccount privateAccount = new PrivateKeyAccount(null, Base58.decode(args[0])); PublicKeyAccount publicAccount = new PublicKeyAccount(null, Base58.decode(args[1])); - byte[] proxyPrivateKey = privateAccount.getProxyPrivateKey(publicAccount.getPublicKey()); - - PrivateKeyAccount proxyAccount = new PrivateKeyAccount(null, proxyPrivateKey); + byte[] rewardSharePrivateKey = privateAccount.getRewardSharePrivateKey(publicAccount.getPublicKey()); + byte[] rewardSharePublicKey = PrivateKeyAccount.toPublicKey(rewardSharePrivateKey); System.out.println(String.format("Private key account: %s", privateAccount.getAddress())); System.out.println(String.format("Public key account: %s", publicAccount.getAddress())); - System.out.println(String.format("Proxy private key: %s", Base58.encode(proxyAccount.getPrivateKey()))); - System.out.println(String.format("Proxy public key: %s", Base58.encode(proxyAccount.getPublicKey()))); + System.out.println(String.format("Reward-share private key: %s", Base58.encode(rewardSharePrivateKey))); + System.out.println(String.format("Reward-share public key: %s", Base58.encode(rewardSharePublicKey))); } } diff --git a/src/main/java/org/qora/account/Account.java b/src/main/java/org/qora/account/Account.java index d4df355e..72c3a6a9 100644 --- a/src/main/java/org/qora/account/Account.java +++ b/src/main/java/org/qora/account/Account.java @@ -6,6 +6,7 @@ import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qora.block.Block; +import org.qora.block.BlockChain; import org.qora.data.account.AccountBalanceData; import org.qora.data.account.AccountData; import org.qora.data.block.BlockData; @@ -212,11 +213,28 @@ public class Account { return Account.isFounder(flags); } - // Forging + // Minting blocks - public boolean canForge() throws DataException { + public boolean canMint() throws DataException { Integer level = this.getLevel(); - return level != null && level > 0; + if (level != null && level >= BlockChain.getInstance().getMinAccountLevelToMint()) + return true; + + if (this.isFounder()) + return true; + + return false; + } + + public boolean canRewardShare() throws DataException { + Integer level = this.getLevel(); + if (level != null && level >= BlockChain.getInstance().getMinAccountLevelToRewardShare()) + return true; + + if (this.isFounder()) + return true; + + return false; } // Account level diff --git a/src/main/java/org/qora/account/PrivateKeyAccount.java b/src/main/java/org/qora/account/PrivateKeyAccount.java index 0a897f2f..5b5413d0 100644 --- a/src/main/java/org/qora/account/PrivateKeyAccount.java +++ b/src/main/java/org/qora/account/PrivateKeyAccount.java @@ -44,6 +44,10 @@ public class PrivateKeyAccount extends PublicKeyAccount { return this.privateKey; } + public static byte[] toPublicKey(byte[] seed) { + return new Ed25519PrivateKeyParameters(seed, 0).generatePublicKey().getEncoded(); + } + public byte[] sign(byte[] message) { byte[] signature = new byte[SIGNATURE_LENGTH]; @@ -65,7 +69,7 @@ public class PrivateKeyAccount extends PublicKeyAccount { return sharedSecret; } - public byte[] getProxyPrivateKey(byte[] publicKey) { + public byte[] getRewardSharePrivateKey(byte[] publicKey) { byte[] sharedSecret = this.getSharedSecret(publicKey); return Crypto.digest(sharedSecret); diff --git a/src/main/java/org/qora/api/model/ApiOnlineAccount.java b/src/main/java/org/qora/api/model/ApiOnlineAccount.java index 2f8a7ca1..94f1f998 100644 --- a/src/main/java/org/qora/api/model/ApiOnlineAccount.java +++ b/src/main/java/org/qora/api/model/ApiOnlineAccount.java @@ -9,8 +9,8 @@ public class ApiOnlineAccount { protected long timestamp; protected byte[] signature; - protected byte[] publicKey; - protected String generatorAddress; + protected byte[] rewardSharePublicKey; + protected String minterAddress; protected String recipientAddress; // Constructors @@ -19,11 +19,11 @@ public class ApiOnlineAccount { protected ApiOnlineAccount() { } - public ApiOnlineAccount(long timestamp, byte[] signature, byte[] publicKey, String generatorAddress, String recipientAddress) { + public ApiOnlineAccount(long timestamp, byte[] signature, byte[] rewardSharePublicKey, String minterAddress, String recipientAddress) { this.timestamp = timestamp; this.signature = signature; - this.publicKey = publicKey; - this.generatorAddress = generatorAddress; + this.rewardSharePublicKey = rewardSharePublicKey; + this.minterAddress = minterAddress; this.recipientAddress = recipientAddress; } @@ -36,11 +36,11 @@ public class ApiOnlineAccount { } public byte[] getPublicKey() { - return this.publicKey; + return this.rewardSharePublicKey; } - public String getGeneratorAddress() { - return this.generatorAddress; + public String getMinterAddress() { + return this.minterAddress; } public String getRecipientAddress() { diff --git a/src/main/java/org/qora/api/model/BlockForgerSummary.java b/src/main/java/org/qora/api/model/BlockForgerSummary.java deleted file mode 100644 index ece5ae99..00000000 --- a/src/main/java/org/qora/api/model/BlockForgerSummary.java +++ /dev/null @@ -1,37 +0,0 @@ -package org.qora.api.model; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; - -import org.qora.crypto.Crypto; - -@XmlAccessorType(XmlAccessType.FIELD) -public class BlockForgerSummary { - - // Properties - - public String generatorAddress; - public int blockCount; - - public String forgedBy; - public String forgedFor; - public byte[] proxyPublicKey; - - // Constructors - - protected BlockForgerSummary() { - } - - public BlockForgerSummary(byte[] generator, int blockCount, byte[] forger, String recipient) { - this.blockCount = blockCount; - - if (recipient == null) { - this.generatorAddress = Crypto.toAddress(generator); - } else { - this.proxyPublicKey = generator; - this.forgedBy = Crypto.toAddress(forger); - this.forgedFor = recipient; - } - } - -} diff --git a/src/main/java/org/qora/api/model/BlockMinterSummary.java b/src/main/java/org/qora/api/model/BlockMinterSummary.java new file mode 100644 index 00000000..862160b0 --- /dev/null +++ b/src/main/java/org/qora/api/model/BlockMinterSummary.java @@ -0,0 +1,42 @@ +package org.qora.api.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; + +import org.qora.crypto.Crypto; + +@XmlAccessorType(XmlAccessType.FIELD) +public class BlockMinterSummary { + + // Properties + + public int blockCount; + + public byte[] mintingAccountPublicKey; + public String mintingAccount; + + public byte[] rewardSharePublicKey; + public String recipientAccount; + + // Constructors + + protected BlockMinterSummary() { + } + + /** Constructs BlockMinterSummary in non-reward-share context. */ + public BlockMinterSummary(byte[] blockMinterPublicKey, int blockCount) { + this.blockCount = blockCount; + + this.mintingAccountPublicKey = blockMinterPublicKey; + this.mintingAccount = Crypto.toAddress(this.mintingAccountPublicKey); + } + + /** Constructs BlockMinterSummary in reward-share context. */ + public BlockMinterSummary(byte[] rewardSharePublicKey, int blockCount, byte[] mintingAccountPublicKey, String recipientAccount) { + this(mintingAccountPublicKey, blockCount); + + this.rewardSharePublicKey = rewardSharePublicKey; + this.recipientAccount = recipientAccount; + } + +} diff --git a/src/main/java/org/qora/api/model/ProxyKeyRequest.java b/src/main/java/org/qora/api/model/RewardShareKeyRequest.java similarity index 65% rename from src/main/java/org/qora/api/model/ProxyKeyRequest.java rename to src/main/java/org/qora/api/model/RewardShareKeyRequest.java index f4b7f79e..c1396724 100644 --- a/src/main/java/org/qora/api/model/ProxyKeyRequest.java +++ b/src/main/java/org/qora/api/model/RewardShareKeyRequest.java @@ -6,15 +6,15 @@ import javax.xml.bind.annotation.XmlAccessorType; import io.swagger.v3.oas.annotations.media.Schema; @XmlAccessorType(XmlAccessType.FIELD) -public class ProxyKeyRequest { +public class RewardShareKeyRequest { @Schema(example = "private_key") - public byte[] generatorPrivateKey; + public byte[] mintingAccountPrivateKey; @Schema(example = "public_key") - public byte[] recipientPublicKey; + public byte[] recipientAccountPublicKey; - public ProxyKeyRequest() { + public RewardShareKeyRequest() { } } diff --git a/src/main/java/org/qora/api/resource/AddressesResource.java b/src/main/java/org/qora/api/resource/AddressesResource.java index b4cd11a1..5ce6c668 100644 --- a/src/main/java/org/qora/api/resource/AddressesResource.java +++ b/src/main/java/org/qora/api/resource/AddressesResource.java @@ -29,14 +29,14 @@ import org.qora.api.ApiErrors; import org.qora.api.ApiException; import org.qora.api.ApiExceptionFactory; import org.qora.api.model.ApiOnlineAccount; -import org.qora.api.model.ProxyKeyRequest; +import org.qora.api.model.RewardShareKeyRequest; import org.qora.asset.Asset; import org.qora.controller.Controller; import org.qora.crypto.Crypto; import org.qora.data.account.AccountData; -import org.qora.data.account.ProxyForgerData; +import org.qora.data.account.RewardShareData; import org.qora.data.network.OnlineAccountData; -import org.qora.data.transaction.ProxyForgingTransactionData; +import org.qora.data.transaction.RewardShareTransactionData; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; @@ -45,7 +45,7 @@ import org.qora.transaction.Transaction; import org.qora.transaction.Transaction.ValidationResult; import org.qora.transform.TransformationException; import org.qora.transform.Transformer; -import org.qora.transform.transaction.ProxyForgingTransactionTransformer; +import org.qora.transform.transaction.RewardShareTransactionTransformer; import org.qora.utils.Base58; @Path("/addresses") @@ -168,18 +168,18 @@ public class AddressesResource { public List getOnlineAccounts() { List onlineAccounts = Controller.getInstance().getOnlineAccounts(); - // Map OnlineAccountData entries to OnlineAccount via proxy-relationship data + // Map OnlineAccountData entries to OnlineAccount via reward-share data try (final Repository repository = RepositoryManager.getRepository()) { List apiOnlineAccounts = new ArrayList<>(); for (OnlineAccountData onlineAccountData : onlineAccounts) { - ProxyForgerData proxyForgerData = repository.getAccountRepository().getProxyForgeData(onlineAccountData.getPublicKey()); - if (proxyForgerData == null) + RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(onlineAccountData.getPublicKey()); + if (rewardShareData == null) // This shouldn't happen? throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.PUBLIC_KEY_NOT_FOUND); apiOnlineAccounts.add(new ApiOnlineAccount(onlineAccountData.getTimestamp(), onlineAccountData.getSignature(), onlineAccountData.getPublicKey(), - proxyForgerData.getForger(), proxyForgerData.getRecipient())); + rewardShareData.getMintingAccount(), rewardShareData.getRecipient())); } return apiOnlineAccounts; @@ -303,19 +303,19 @@ public class AddressesResource { } @GET - @Path("/proxying") + @Path("/rewardshares") @Operation( - summary = "List proxy forging relationships", - description = "Returns list of accounts, with reward share percentage and proxy public key.", + summary = "List reward-share relationships", + description = "Returns list of accounts, with reward-share percentage and reward-share public key.", responses = { @ApiResponse( - content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = ProxyForgerData.class))) + content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = RewardShareData.class))) ) } ) @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE}) - public List getProxying(@QueryParam("proxiedFor") List recipients, - @QueryParam("proxiedBy") List forgers, + public List getRewardShares(@QueryParam("minters") List mintingAccounts, + @QueryParam("recipients") List recipientAccounts, @QueryParam("involving") List addresses, @Parameter( ref = "limit" @@ -325,23 +325,23 @@ public class AddressesResource { ref = "reverse" ) @QueryParam("reverse") Boolean reverse) { try (final Repository repository = RepositoryManager.getRepository()) { - return repository.getAccountRepository().findProxyAccounts(recipients, forgers, addresses, limit, offset, reverse); + return repository.getAccountRepository().findRewardShares(mintingAccounts, recipientAccounts, addresses, limit, offset, reverse); } catch (DataException e) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); } } @POST - @Path("/proxykey") + @Path("/rewardsharekey") @Operation( - summary = "Calculate proxy private key", - description = "Calculates proxy private key using passed generator's private key and recipient's public key", + summary = "Calculate reward-share private key", + description = "Calculates reward-share private key using passed minting account's private key and recipient account's public key", requestBody = @RequestBody( required = true, content = @Content( mediaType = MediaType.APPLICATION_JSON, schema = @Schema( - implementation = ProxyKeyRequest.class + implementation = RewardShareKeyRequest.class ) ) ), @@ -352,39 +352,39 @@ public class AddressesResource { } ) @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.INVALID_PUBLIC_KEY, ApiError.REPOSITORY_ISSUE}) - public String calculateProxyKey(ProxyKeyRequest proxyKeyRequest) { - byte[] generatorKey = proxyKeyRequest.generatorPrivateKey; - byte[] recipientKey = proxyKeyRequest.recipientPublicKey; + public String calculateRewardShareKey(RewardShareKeyRequest rewardShareKeyRequest) { + byte[] mintingPrivateKey = rewardShareKeyRequest.mintingAccountPrivateKey; + byte[] recipientPublicKey = rewardShareKeyRequest.recipientAccountPublicKey; - if (generatorKey == null || generatorKey.length != Transformer.PRIVATE_KEY_LENGTH) + if (mintingPrivateKey == null || mintingPrivateKey.length != Transformer.PRIVATE_KEY_LENGTH) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY); - if (recipientKey == null || recipientKey.length != Transformer.PUBLIC_KEY_LENGTH) + if (recipientPublicKey == null || recipientPublicKey.length != Transformer.PUBLIC_KEY_LENGTH) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PUBLIC_KEY); - PrivateKeyAccount generator = new PrivateKeyAccount(null, generatorKey); + PrivateKeyAccount mintingAccount = new PrivateKeyAccount(null, mintingPrivateKey); - byte[] proxyPrivateKey = generator.getProxyPrivateKey(recipientKey); + byte[] rewardSharePrivateKey = mintingAccount.getRewardSharePrivateKey(recipientPublicKey); - return Base58.encode(proxyPrivateKey); + return Base58.encode(rewardSharePrivateKey); } @POST - @Path("/proxyforging") + @Path("/rewardshare") @Operation( - summary = "Build raw, unsigned, PROXY_FORGING transaction", + summary = "Build raw, unsigned, REWARD_SHARE transaction", requestBody = @RequestBody( required = true, content = @Content( mediaType = MediaType.APPLICATION_JSON, schema = @Schema( - implementation = ProxyForgingTransactionData.class + implementation = RewardShareTransactionData.class ) ) ), responses = { @ApiResponse( - description = "raw, unsigned, PROXY_FORGING transaction encoded in Base58", + description = "raw, unsigned, REWARD_SHARE transaction encoded in Base58", content = @Content( mediaType = MediaType.TEXT_PLAIN, schema = @Schema( @@ -395,7 +395,7 @@ public class AddressesResource { } ) @ApiErrors({ApiError.NON_PRODUCTION, ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE}) - public String proxyForging(ProxyForgingTransactionData transactionData) { + public String rewardShare(RewardShareTransactionData transactionData) { if (Settings.getInstance().isApiRestricted()) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.NON_PRODUCTION); @@ -406,7 +406,7 @@ public class AddressesResource { if (result != ValidationResult.OK) throw TransactionsResource.createTransactionInvalidException(request, result); - byte[] bytes = ProxyForgingTransactionTransformer.toBytes(transactionData); + byte[] bytes = RewardShareTransactionTransformer.toBytes(transactionData); return Base58.encode(bytes); } catch (TransformationException e) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e); diff --git a/src/main/java/org/qora/api/resource/AdminResource.java b/src/main/java/org/qora/api/resource/AdminResource.java index ebea9e8b..f7b11ade 100644 --- a/src/main/java/org/qora/api/resource/AdminResource.java +++ b/src/main/java/org/qora/api/resource/AdminResource.java @@ -50,8 +50,8 @@ import org.qora.controller.Synchronizer.SynchronizationResult; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; -import org.qora.data.account.ForgingAccountData; -import org.qora.data.account.ProxyForgerData; +import org.qora.data.account.MintingAccountData; +import org.qora.data.account.RewardShareData; import org.qora.network.Network; import org.qora.network.Peer; import org.qora.network.PeerAddress; @@ -189,45 +189,45 @@ public class AdminResource { } @GET - @Path("/forgingaccounts") + @Path("/mintingaccounts") @Operation( - summary = "List public keys of accounts used to forge by BlockGenerator", + summary = "List public keys of accounts used to mint blocks by BlockMinter", description = "Returns PUBLIC keys of accounts for safety.", responses = { @ApiResponse( - content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = ForgingAccountData.class))) + content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = MintingAccountData.class))) ) } ) @ApiErrors({ApiError.REPOSITORY_ISSUE}) - public List getForgingAccounts() { + public List getMintingAccounts() { try (final Repository repository = RepositoryManager.getRepository()) { - List forgingAccounts = repository.getAccountRepository().getForgingAccounts(); + List mintingAccounts = repository.getAccountRepository().getMintingAccounts(); - // Expand with proxy forging data where appropriate - forgingAccounts = forgingAccounts.stream().map(forgingAccountData -> { - byte[] publicKey = forgingAccountData.getPublicKey(); + // Expand with reward-share data where appropriate + mintingAccounts = mintingAccounts.stream().map(mintingAccountData -> { + byte[] publicKey = mintingAccountData.getPublicKey(); - ProxyForgerData proxyForgerData = null; + RewardShareData rewardShareData = null; try { - proxyForgerData = repository.getAccountRepository().getProxyForgeData(publicKey); + rewardShareData = repository.getAccountRepository().getRewardShare(publicKey); } catch (DataException e) { // ignore } - return new ForgingAccountData(forgingAccountData.getSeed(), proxyForgerData); + return new MintingAccountData(mintingAccountData.getPrivateKey(), rewardShareData); }).collect(Collectors.toList()); - return forgingAccounts; + return mintingAccounts; } catch (DataException e) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); } } @POST - @Path("/forgingaccounts") + @Path("/mintingaccounts") @Operation( - summary = "Add private key of account to use to forge by BlockGenerator", + summary = "Add private key of account/reward-share for use by BlockMinter to mint blocks", requestBody = @RequestBody( required = true, content = @Content( @@ -244,20 +244,20 @@ public class AdminResource { } ) @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.REPOSITORY_ISSUE}) - public String addForgingAccount(String seed58) { + public String addMintingAccount(String seed58) { try (final Repository repository = RepositoryManager.getRepository()) { byte[] seed = Base58.decode(seed58.trim()); // Check seed is valid - PrivateKeyAccount forgingAccount = new PrivateKeyAccount(repository, seed); + PrivateKeyAccount mintingAccount = new PrivateKeyAccount(repository, seed); - // Account must derive to known proxy forging public key - if (!repository.getAccountRepository().isProxyPublicKey(forgingAccount.getPublicKey())) + // Qortal: account must derive to known reward-share public key + if (!repository.getAccountRepository().isRewardSharePublicKey(mintingAccount.getPublicKey())) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY); - ForgingAccountData forgingAccountData = new ForgingAccountData(seed); + MintingAccountData mintingAccountData = new MintingAccountData(seed); - repository.getAccountRepository().save(forgingAccountData); + repository.getAccountRepository().save(mintingAccountData); repository.saveChanges(); } catch (IllegalArgumentException e) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY, e); @@ -269,9 +269,9 @@ public class AdminResource { } @DELETE - @Path("/forgingaccounts") + @Path("/mintingaccounts") @Operation( - summary = "Remove account from use to forge by BlockGenerator, using private key", + summary = "Remove account/reward-share from use by BlockMinter, using private key", requestBody = @RequestBody( required = true, content = @Content( @@ -288,7 +288,7 @@ public class AdminResource { } ) @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.REPOSITORY_ISSUE}) - public String deleteForgingAccount(String seed58) { + public String deleteMintingAccount(String seed58) { try (final Repository repository = RepositoryManager.getRepository()) { byte[] seed = Base58.decode(seed58.trim()); diff --git a/src/main/java/org/qora/api/resource/BlocksResource.java b/src/main/java/org/qora/api/resource/BlocksResource.java index ef6d1d6b..c1664086 100644 --- a/src/main/java/org/qora/api/resource/BlocksResource.java +++ b/src/main/java/org/qora/api/resource/BlocksResource.java @@ -24,7 +24,7 @@ import org.qora.api.ApiError; import org.qora.api.ApiErrors; import org.qora.api.ApiException; import org.qora.api.ApiExceptionFactory; -import org.qora.api.model.BlockForgerSummary; +import org.qora.api.model.BlockMinterSummary; import org.qora.crypto.Crypto; import org.qora.data.account.AccountData; import org.qora.data.block.BlockData; @@ -421,9 +421,9 @@ public class BlocksResource { } @GET - @Path("/forger/{address}") + @Path("/minter/{address}") @Operation( - summary = "Fetch blocks forged by address", + summary = "Fetch blocks minted by address", responses = { @ApiResponse( description = "blocks", @@ -438,7 +438,7 @@ public class BlocksResource { } ) @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.PUBLIC_KEY_NOT_FOUND, ApiError.REPOSITORY_ISSUE}) - public List getBlocksByForger(@PathParam("address") String address, @Parameter( + public List getBlocksByMinter(@PathParam("address") String address, @Parameter( ref = "limit" ) @QueryParam("limit") Integer limit, @Parameter( ref = "offset" @@ -454,7 +454,7 @@ public class BlocksResource { if (accountData == null || accountData.getPublicKey() == null) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.PUBLIC_KEY_NOT_FOUND); - return repository.getBlockRepository().getBlocksWithGenerator(accountData.getPublicKey(), limit, offset, reverse); + return repository.getBlockRepository().getBlocksByMinter(accountData.getPublicKey(), limit, offset, reverse); } catch (ApiException e) { throw e; } catch (DataException e) { @@ -463,23 +463,23 @@ public class BlocksResource { } @GET - @Path("/forgers") + @Path("/minters") @Operation( - summary = "Show summary of block forgers", - description = "Returns count of blocks forged, optionally limited to generators/recipients in passed address(es).", + summary = "Show summary of block minters", + description = "Returns count of blocks minted, optionally limited to minters/recipients in passed address(es).", responses = { @ApiResponse( content = @Content( array = @ArraySchema( schema = @Schema( - implementation = BlockForgerSummary.class + implementation = BlockMinterSummary.class ) ) ) ) } ) - public List getBlockForgers(@QueryParam("address") List addresses, + public List getBlockMinters(@QueryParam("address") List addresses, @Parameter( ref = "limit" ) @QueryParam("limit") Integer limit, @Parameter( @@ -492,7 +492,7 @@ public class BlocksResource { if (!Crypto.isValidAddress(address)) throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS); - return repository.getBlockRepository().getBlockForgers(addresses, limit, offset, reverse); + return repository.getBlockRepository().getBlockMinters(addresses, limit, offset, reverse); } catch (DataException e) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); } diff --git a/src/main/java/org/qora/block/Block.java b/src/main/java/org/qora/block/Block.java index 9c70e4a8..1f00d537 100644 --- a/src/main/java/org/qora/block/Block.java +++ b/src/main/java/org/qora/block/Block.java @@ -28,7 +28,7 @@ import org.qora.controller.Controller; import org.qora.crypto.Crypto; import org.qora.data.account.AccountBalanceData; import org.qora.data.account.AccountData; -import org.qora.data.account.ProxyForgerData; +import org.qora.data.account.RewardShareData; import org.qora.data.at.ATData; import org.qora.data.at.ATStateData; import org.qora.data.block.BlockData; @@ -73,7 +73,7 @@ public class Block { TIMESTAMP_INCORRECT(24), VERSION_INCORRECT(30), FEATURE_NOT_YET_RELEASED(31), - GENERATOR_NOT_ACCEPTED(41), + MINTER_NOT_ACCEPTED(41), GENESIS_TRANSACTIONS_INVALID(50), TRANSACTION_TIMESTAMP_INVALID(51), TRANSACTION_INVALID(52), @@ -103,7 +103,7 @@ public class Block { // Properties protected Repository repository; protected BlockData blockData; - protected PublicKeyAccount generator; + protected PublicKeyAccount minter; // Other properties private static final Logger LOGGER = LogManager.getLogger(Block.class); @@ -125,13 +125,13 @@ public class Block { /** Lazy-instantiated expanded info on block's online accounts. */ class ExpandedAccount { - final ProxyForgerData proxyForgerData; - final boolean isRecipientAlsoForger; + final RewardShareData rewardShareData; + final boolean isRecipientAlsoMinter; - final Account forgerAccount; - final AccountData forgerAccountData; - final boolean isForgerFounder; - final BigDecimal forgerQoraAmount; + final Account mintingAccount; + final AccountData mintingAccountData; + final boolean isMinterFounder; + final BigDecimal minterQoraAmount; final int shareBin; final Account recipientAccount; @@ -141,25 +141,25 @@ public class Block { ExpandedAccount(Repository repository, int accountIndex) throws DataException { final List sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel(); - this.proxyForgerData = repository.getAccountRepository().getProxyAccountByIndex(accountIndex); + this.rewardShareData = repository.getAccountRepository().getRewardShareByIndex(accountIndex); - this.forgerAccount = new PublicKeyAccount(repository, this.proxyForgerData.getForgerPublicKey()); - this.recipientAccount = new Account(repository, this.proxyForgerData.getRecipient()); + this.mintingAccount = new PublicKeyAccount(repository, this.rewardShareData.getMinterPublicKey()); + this.recipientAccount = new Account(repository, this.rewardShareData.getRecipient()); - AccountBalanceData qoraBalanceData = repository.getAccountRepository().getBalance(this.forgerAccount.getAddress(), Asset.LEGACY_QORA); + AccountBalanceData qoraBalanceData = repository.getAccountRepository().getBalance(this.mintingAccount.getAddress(), Asset.LEGACY_QORA); if (qoraBalanceData != null && qoraBalanceData.getBalance() != null && qoraBalanceData.getBalance().compareTo(BigDecimal.ZERO) > 0) - this.forgerQoraAmount = qoraBalanceData.getBalance(); + this.minterQoraAmount = qoraBalanceData.getBalance(); else - this.forgerQoraAmount = null; + this.minterQoraAmount = null; - this.forgerAccountData = repository.getAccountRepository().getAccount(this.forgerAccount.getAddress()); - this.isForgerFounder = Account.isFounder(forgerAccountData.getFlags()); + this.mintingAccountData = repository.getAccountRepository().getAccount(this.mintingAccount.getAddress()); + this.isMinterFounder = Account.isFounder(mintingAccountData.getFlags()); int currentShareBin = -1; - if (!this.isForgerFounder) + if (!this.isMinterFounder) for (int s = 0; s < sharesByLevel.size(); ++s) - if (sharesByLevel.get(s).levels.contains(this.forgerAccountData.getLevel())) { + if (sharesByLevel.get(s).levels.contains(this.mintingAccountData.getLevel())) { currentShareBin = s; break; } @@ -169,23 +169,23 @@ public class Block { this.recipientAccountData = repository.getAccountRepository().getAccount(this.recipientAccount.getAddress()); this.isRecipientFounder = Account.isFounder(recipientAccountData.getFlags()); - this.isRecipientAlsoForger = this.forgerAccountData.getAddress().equals(this.recipientAccountData.getAddress()); + this.isRecipientAlsoMinter = this.mintingAccountData.getAddress().equals(this.recipientAccountData.getAddress()); } void distribute(BigDecimal accountAmount) throws DataException { final BigDecimal oneHundred = BigDecimal.valueOf(100L); - if (this.forgerAccount.getAddress().equals(this.recipientAccount.getAddress())) { - // forger & recipient the same - simpler case - LOGGER.trace(() -> String.format("Forger/recipient account %s share: %s", this.forgerAccount.getAddress(), accountAmount.toPlainString())); - this.forgerAccount.setConfirmedBalance(Asset.QORT, this.forgerAccount.getConfirmedBalance(Asset.QORT).add(accountAmount)); + if (this.mintingAccount.getAddress().equals(this.recipientAccount.getAddress())) { + // minter & recipient the same - simpler case + LOGGER.trace(() -> String.format("Minter/recipient account %s share: %s", this.mintingAccount.getAddress(), accountAmount.toPlainString())); + this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(accountAmount)); } else { - // forger & recipient different - extra work needed - BigDecimal recipientAmount = accountAmount.multiply(this.proxyForgerData.getShare()).divide(oneHundred, RoundingMode.DOWN); - BigDecimal forgerAmount = accountAmount.subtract(recipientAmount); + // minter & recipient different - extra work needed + BigDecimal recipientAmount = accountAmount.multiply(this.rewardShareData.getSharePercent()).divide(oneHundred, RoundingMode.DOWN); + BigDecimal minterAmount = accountAmount.subtract(recipientAmount); - LOGGER.trace(() -> String.format("Forger account %s share: %s", this.forgerAccount.getAddress(), forgerAmount.toPlainString())); - this.forgerAccount.setConfirmedBalance(Asset.QORT, this.forgerAccount.getConfirmedBalance(Asset.QORT).add(forgerAmount)); + LOGGER.trace(() -> String.format("Minter account %s share: %s", this.mintingAccount.getAddress(), minterAmount.toPlainString())); + this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(minterAmount)); LOGGER.trace(() -> String.format("Recipient account %s share: %s", this.recipientAccount.getAddress(), recipientAmount.toPlainString())); this.recipientAccount.setConfirmedBalance(Asset.QORT, this.recipientAccount.getConfirmedBalance(Asset.QORT).add(recipientAmount)); @@ -220,7 +220,7 @@ public class Block { public Block(Repository repository, BlockData blockData) throws DataException { this.repository = repository; this.blockData = blockData; - this.generator = new PublicKeyAccount(repository, blockData.getGeneratorPublicKey()); + this.minter = new PublicKeyAccount(repository, blockData.getMinterPublicKey()); } /** @@ -257,18 +257,18 @@ public class Block { /** * Constructs Block-handling object with basic, initial values. *

- * This constructor typically used when generating a new block. + * This constructor typically used when minting a new block. *

* Note that CIYAM ATs will be executed and AT-Transactions prepended to this block, along with AT state data and fees. * * @param repository * @param parentBlockData - * @param generator + * @param minter * @throws DataException */ - public Block(Repository repository, BlockData parentBlockData, PrivateKeyAccount generator) throws DataException { + public Block(Repository repository, BlockData parentBlockData, PrivateKeyAccount minter) throws DataException { this.repository = repository; - this.generator = generator; + this.minter = minter; Block parentBlock = new Block(repository, parentBlockData); @@ -287,14 +287,14 @@ public class Block { onlineAccountsTimestamp = onlineAccountData.getTimestamp(); } - // Map using account index (in list of proxy forger accounts) + // Map using index into sorted list of reward-shares as key Map indexedOnlineAccounts = new HashMap<>(); for (OnlineAccountData onlineAccountData : onlineAccounts) { // Disregard online accounts with different timestamps if (onlineAccountData.getTimestamp() != onlineAccountsTimestamp) continue; - int accountIndex = repository.getAccountRepository().getProxyAccountIndex(onlineAccountData.getPublicKey()); + int accountIndex = repository.getAccountRepository().getRewardShareIndex(onlineAccountData.getPublicKey()); indexedOnlineAccounts.put(accountIndex, onlineAccountData); } List accountIndexes = new ArrayList<>(indexedOnlineAccounts.keySet()); @@ -314,14 +314,14 @@ public class Block { System.arraycopy(onlineAccountData.getSignature(), 0, onlineAccountsSignatures, i * Transformer.SIGNATURE_LENGTH, Transformer.SIGNATURE_LENGTH); } - byte[] generatorSignature; + byte[] minterSignature; try { - generatorSignature = generator.sign(BlockTransformer.getBytesForGeneratorSignature(parentBlockData.getGeneratorSignature(), generator, encodedOnlineAccounts)); + minterSignature = minter.sign(BlockTransformer.getBytesForMinterSignature(parentBlockData.getMinterSignature(), minter, encodedOnlineAccounts)); } catch (TransformationException e) { - throw new DataException("Unable to calculate next block generator signature", e); + throw new DataException("Unable to calculate next block minter signature", e); } - long timestamp = calcTimestamp(parentBlockData, generator.getPublicKey()); + long timestamp = calcTimestamp(parentBlockData, minter.getPublicKey()); int transactionCount = 0; byte[] transactionsSignature = null; @@ -335,7 +335,7 @@ public class Block { // This instance used for AT processing this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, - generator.getPublicKey(), generatorSignature, atCount, atFees, + minter.getPublicKey(), minterSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures); // Requires this.blockData and this.transactions, sets this.ourAtStates and this.ourAtFees @@ -348,22 +348,22 @@ public class Block { // Rebuild blockData using post-AT-execute data this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, - generator.getPublicKey(), generatorSignature, atCount, atFees, + minter.getPublicKey(), minterSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures); } /** - * Construct another block using this block as template, but with different generator account. + * Construct another block using this block as template, but with different minting account. *

* NOTE: uses the same transactions list, AT states, etc. * - * @param generator + * @param minter * @return * @throws DataException */ - public Block regenerate(PrivateKeyAccount generator) throws DataException { + public Block newMinter(PrivateKeyAccount minter) throws DataException { Block newBlock = new Block(this.repository, this.blockData); - newBlock.generator = generator; + newBlock.minter = minter; BlockData parentBlockData = this.getParent(); @@ -376,14 +376,14 @@ public class Block { int version = this.blockData.getVersion(); byte[] reference = this.blockData.getReference(); - byte[] generatorSignature; + byte[] minterSignature; try { - generatorSignature = generator.sign(BlockTransformer.getBytesForGeneratorSignature(parentBlockData.getGeneratorSignature(), generator, this.blockData.getEncodedOnlineAccounts())); + minterSignature = minter.sign(BlockTransformer.getBytesForMinterSignature(parentBlockData.getMinterSignature(), minter, this.blockData.getEncodedOnlineAccounts())); } catch (TransformationException e) { - throw new DataException("Unable to calculate next block generator signature", e); + throw new DataException("Unable to calculate next block's minter signature", e); } - long timestamp = calcTimestamp(parentBlockData, generator.getPublicKey()); + long timestamp = calcTimestamp(parentBlockData, minter.getPublicKey()); newBlock.transactions = this.transactions; int transactionCount = this.blockData.getTransactionCount(); @@ -400,7 +400,7 @@ public class Block { byte[] onlineAccountsSignatures = this.blockData.getOnlineAccountsSignatures(); newBlock.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, - generator.getPublicKey(), generatorSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures); + minter.getPublicKey(), minterSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures); // Resign to update transactions signature newBlock.sign(); @@ -414,22 +414,22 @@ public class Block { return this.blockData; } - public PublicKeyAccount getGenerator() { - return this.generator; + public PublicKeyAccount getMinter() { + return this.minter; } // More information /** - * Return composite block signature (generatorSignature + transactionsSignature). + * Return composite block signature (minterSignature + transactionsSignature). * * @return byte[], or null if either component signature is null. */ public byte[] getSignature() { - if (this.blockData.getGeneratorSignature() == null || this.blockData.getTransactionsSignature() == null) + if (this.blockData.getMinterSignature() == null || this.blockData.getTransactionsSignature() == null) return null; - return Bytes.concat(this.blockData.getGeneratorSignature(), this.blockData.getTransactionsSignature()); + return Bytes.concat(this.blockData.getMinterSignature(), this.blockData.getTransactionsSignature()); } /** @@ -570,25 +570,25 @@ public class Block { /** * Add a transaction to the block. *

- * Used when constructing a new block during forging. + * Used when constructing a new block during minting. *

- * Requires block's {@code generator} being a {@code PrivateKeyAccount} so block's transactions signature can be recalculated. + * Requires block's {@code minter} being a {@code PrivateKeyAccount} so block's transactions signature can be recalculated. * * @param transactionData * @return true if transaction successfully added to block, false otherwise * @throws IllegalStateException - * if block's {@code generator} is not a {@code PrivateKeyAccount}. + * if block's {@code minter} is not a {@code PrivateKeyAccount}. */ public boolean addTransaction(TransactionData transactionData) { // Can't add to transactions if we haven't loaded existing ones yet if (this.transactions == null) throw new IllegalStateException("Attempted to add transaction to partially loaded database Block"); - if (!(this.generator instanceof PrivateKeyAccount)) - throw new IllegalStateException("Block's generator has no private key"); + if (!(this.minter instanceof PrivateKeyAccount)) + throw new IllegalStateException("Block's minter is not PrivateKeyAccount - can't sign!"); - if (this.blockData.getGeneratorSignature() == null) - throw new IllegalStateException("Cannot calculate transactions signature as block has no generator signature"); + if (this.blockData.getMinterSignature() == null) + throw new IllegalStateException("Cannot calculate transactions signature as block has no minter signature"); // Already added? (Check using signature) if (this.transactions.stream().anyMatch(transaction -> Arrays.equals(transaction.getTransactionData().getSignature(), transactionData.getSignature()))) @@ -623,24 +623,24 @@ public class Block { /** * Remove a transaction from the block. *

- * Used when constructing a new block during forging. + * Used when constructing a new block during minting. *

- * Requires block's {@code generator} being a {@code PrivateKeyAccount} so block's transactions signature can be recalculated. + * Requires block's {@code minter} being a {@code PrivateKeyAccount} so block's transactions signature can be recalculated. * * @param transactionData * @throws IllegalStateException - * if block's {@code generator} is not a {@code PrivateKeyAccount}. + * if block's {@code minter} is not a {@code PrivateKeyAccount}. */ public void deleteTransaction(TransactionData transactionData) { // Can't add to transactions if we haven't loaded existing ones yet if (this.transactions == null) throw new IllegalStateException("Attempted to add transaction to partially loaded database Block"); - if (!(this.generator instanceof PrivateKeyAccount)) - throw new IllegalStateException("Block's generator has no private key"); + if (!(this.minter instanceof PrivateKeyAccount)) + throw new IllegalStateException("Block's minter is not a PrivateKeyAccount - can't sign!"); - if (this.blockData.getGeneratorSignature() == null) - throw new IllegalStateException("Cannot calculate transactions signature as block has no generator signature"); + if (this.blockData.getMinterSignature() == null) + throw new IllegalStateException("Cannot calculate transactions signature as block has no minter signature"); // Attempt to remove from block (Check using signature) boolean wasElementRemoved = this.transactions.removeIf(transaction -> Arrays.equals(transaction.getTransactionData().getSignature(), transactionData.getSignature())); @@ -662,54 +662,54 @@ public class Block { } /** - * Recalculate block's generator signature. + * Recalculate block's minter signature. *

- * Requires block's {@code generator} being a {@code PrivateKeyAccount}. + * Requires block's {@code minter} being a {@code PrivateKeyAccount}. *

- * Generator signature is made by the generator signing the following data: + * Minter signature is made by the minter signing the following data: *

- * previous block's generator signature + this block's generating balance + generator's public key + * previous block's minter signature + minter's public key + (encoded) online-accounts data *

- * (Previous block's generator signature is extracted from this block's reference). + * (Previous block's minter signature is extracted from this block's reference). * * @throws IllegalStateException - * if block's {@code generator} is not a {@code PrivateKeyAccount}. + * if block's {@code minter} is not a {@code PrivateKeyAccount}. * @throws RuntimeException - * if somehow the generator signature cannot be calculated + * if somehow the minter signature cannot be calculated */ - protected void calcGeneratorSignature() { - if (!(this.generator instanceof PrivateKeyAccount)) - throw new IllegalStateException("Block's generator has no private key"); + protected void calcMinterSignature() { + if (!(this.minter instanceof PrivateKeyAccount)) + throw new IllegalStateException("Block's minter is not a PrivateKeyAccount - can't sign!"); try { - this.blockData.setGeneratorSignature(((PrivateKeyAccount) this.generator).sign(BlockTransformer.getBytesForGeneratorSignature(this.blockData))); + this.blockData.setMinterSignature(((PrivateKeyAccount) this.minter).sign(BlockTransformer.getBytesForMinterSignature(this.blockData))); } catch (TransformationException e) { - throw new RuntimeException("Unable to calculate block's generator signature", e); + throw new RuntimeException("Unable to calculate block's minter signature", e); } } /** * Recalculate block's transactions signature. *

- * Requires block's {@code generator} being a {@code PrivateKeyAccount}. + * Requires block's {@code minter} being a {@code PrivateKeyAccount}. * * @throws IllegalStateException - * if block's {@code generator} is not a {@code PrivateKeyAccount}. + * if block's {@code minter} is not a {@code PrivateKeyAccount}. * @throws RuntimeException * if somehow the transactions signature cannot be calculated */ protected void calcTransactionsSignature() { - if (!(this.generator instanceof PrivateKeyAccount)) - throw new IllegalStateException("Block's generator has no private key"); + if (!(this.minter instanceof PrivateKeyAccount)) + throw new IllegalStateException("Block's minter is not a PrivateKeyAccount - can't sign!"); try { - this.blockData.setTransactionsSignature(((PrivateKeyAccount) this.generator).sign(BlockTransformer.getBytesForTransactionsSignature(this))); + this.blockData.setTransactionsSignature(((PrivateKeyAccount) this.minter).sign(BlockTransformer.getBytesForTransactionsSignature(this))); } catch (TransformationException e) { throw new RuntimeException("Unable to calculate block's transactions signature", e); } } - public static byte[] calcIdealGeneratorPublicKey(int parentBlockHeight, byte[] parentBlockSignature) { + public static byte[] calcIdealMinterPublicKey(int parentBlockHeight, byte[] parentBlockSignature) { return Crypto.digest(Bytes.concat(Longs.toByteArray(parentBlockHeight), parentBlockSignature)); } @@ -718,14 +718,14 @@ public class Block { } public static BigInteger calcKeyDistance(int parentHeight, byte[] parentBlockSignature, byte[] publicKey) { - byte[] idealKey = calcIdealGeneratorPublicKey(parentHeight, parentBlockSignature); + byte[] idealKey = calcIdealMinterPublicKey(parentHeight, parentBlockSignature); byte[] perturbedKey = calcHeightPerturbedPublicKey(parentHeight + 1, publicKey); return MAX_DISTANCE.subtract(new BigInteger(idealKey).subtract(new BigInteger(perturbedKey)).abs()); } public static BigInteger calcBlockWeight(int parentHeight, byte[] parentBlockSignature, BlockSummaryData blockSummaryData) { - BigInteger keyDistance = calcKeyDistance(parentHeight, parentBlockSignature, blockSummaryData.getGeneratorPublicKey()); + BigInteger keyDistance = calcKeyDistance(parentHeight, parentBlockSignature, blockSummaryData.getMinterPublicKey()); return BigInteger.valueOf(blockSummaryData.getOnlineAccountsCount()).shiftLeft(ACCOUNTS_COUNT_SHIFT).add(keyDistance); } @@ -744,19 +744,20 @@ public class Block { } /** - * Returns timestamp based on previous block and this block's generator. + * Returns timestamp based on previous block and this block's minter. *

- * Uses same proportion of this block's generator from 'ideal' generator - * with min to max target block periods, added to previous block's timestamp. + * Uses distance of this block's minter from 'ideal' minter, + * along with min to max target block periods, + * added to previous block's timestamp. *

* Example:
- * This block's generator is 20% of max distance from 'ideal' generator.
+ * This block's minter is 20% of max distance from 'ideal' minter.
* Min/Max block periods are 30s and 90s respectively.
* 20% of (90s - 30s) is 12s
* So this block's timestamp is previous block's timestamp + 30s + 12s. */ - public static long calcTimestamp(BlockData parentBlockData, byte[] generatorPublicKey) { - BigInteger distance = calcKeyDistance(parentBlockData.getHeight(), parentBlockData.getSignature(), generatorPublicKey); + public static long calcTimestamp(BlockData parentBlockData, byte[] minterPublicKey) { + BigInteger distance = calcKeyDistance(parentBlockData.getHeight(), parentBlockData.getSignature(), minterPublicKey); final int thisHeight = parentBlockData.getHeight() + 1; BlockTimingByHeight blockTiming = BlockChain.getInstance().getBlockTimingByHeight(thisHeight); @@ -777,15 +778,15 @@ public class Block { } /** - * Recalculate block's generator and transactions signatures, thus giving block full signature. + * Recalculate block's minter and transactions signatures, thus giving block full signature. *

- * Note: Block instance must have been constructed with a PrivateKeyAccount generator or this call will throw an IllegalStateException. + * Note: Block instance must have been constructed with a PrivateKeyAccount minter or this call will throw an IllegalStateException. * * @throws IllegalStateException - * if block's {@code generator} is not a {@code PrivateKeyAccount}. + * if block's {@code minter} is not a {@code PrivateKeyAccount}. */ public void sign() { - this.calcGeneratorSignature(); + this.calcMinterSignature(); this.calcTransactionsSignature(); this.blockData.setSignature(this.getSignature()); @@ -794,16 +795,16 @@ public class Block { /** * Returns whether this block's signatures are valid. * - * @return true if both generator and transaction signatures are valid, false otherwise + * @return true if both minter and transaction signatures are valid, false otherwise */ public boolean isSignatureValid() { try { - // Check generator's signature first - if (!this.generator.verify(this.blockData.getGeneratorSignature(), BlockTransformer.getBytesForGeneratorSignature(this.blockData))) + // Check minter's signature first + if (!this.minter.verify(this.blockData.getMinterSignature(), BlockTransformer.getBytesForMinterSignature(this.blockData))) return false; // Check transactions signature - if (!this.generator.verify(this.blockData.getTransactionsSignature(), BlockTransformer.getBytesForTransactionsSignature(this))) + if (!this.minter.verify(this.blockData.getTransactionsSignature(), BlockTransformer.getBytesForTransactionsSignature(this))) return false; } catch (TransformationException e) { return false; @@ -815,7 +816,7 @@ public class Block { /** * Returns whether Block's timestamp is valid. *

- * Used by BlockGenerator to check whether it's time to forge new block, + * Used by BlockMinter to check whether it's time to mint a new block, * and also used by Block.isValid for checks (if not a testchain). * * @return ValidationResult.OK if timestamp valid, or some other ValidationResult otherwise. @@ -839,7 +840,7 @@ public class Block { if (this.blockData.getTimestamp() < Block.calcMinimumTimestamp(parentBlockData)) return ValidationResult.TIMESTAMP_TOO_SOON; - long expectedTimestamp = calcTimestamp(parentBlockData, this.blockData.getGeneratorPublicKey()); + long expectedTimestamp = calcTimestamp(parentBlockData, this.blockData.getMinterPublicKey()); if (this.blockData.getTimestamp() != expectedTimestamp) return ValidationResult.TIMESTAMP_INCORRECT; @@ -857,18 +858,18 @@ public class Block { if (accountIndexes.size() != this.blockData.getOnlineAccountsCount()) return ValidationResult.ONLINE_ACCOUNTS_INVALID; - List expandedAccounts = new ArrayList<>(); + List expandedAccounts = new ArrayList<>(); IntIterator iterator = accountIndexes.iterator(); while (iterator.hasNext()) { int accountIndex = iterator.next(); - ProxyForgerData proxyAccountData = repository.getAccountRepository().getProxyAccountByIndex(accountIndex); + RewardShareData rewardShareData = repository.getAccountRepository().getRewardShareByIndex(accountIndex); // Check that claimed online account actually exists - if (proxyAccountData == null) + if (rewardShareData == null) return ValidationResult.ONLINE_ACCOUNT_UNKNOWN; - expandedAccounts.add(proxyAccountData); + expandedAccounts.add(rewardShareData); } // Possibly check signatures if block is recent @@ -885,7 +886,7 @@ public class Block { byte[] message = Longs.toByteArray(this.blockData.getOnlineAccountsTimestamp()); for (int i = 0; i < onlineAccountsSignatures.size(); ++i) { - PublicKeyAccount account = new PublicKeyAccount(null, expandedAccounts.get(i).getProxyPublicKey()); + PublicKeyAccount account = new PublicKeyAccount(null, expandedAccounts.get(i).getRewardSharePublicKey()); byte[] signature = onlineAccountsSignatures.get(i); if (!account.verify(signature, message)) @@ -900,7 +901,7 @@ public class Block { /** * Returns whether Block is valid. *

- * Performs various tests like checking for parent block, correct block timestamp, version, generating balance, etc. + * Performs various tests like checking for parent block, correct block timestamp, version, etc. *

* Checks block's transactions by testing their validity then processing them.
* Hence uses a repository savepoint during execution. @@ -941,9 +942,9 @@ public class Block { if (this.blockData.getVersion() < 2 && this.blockData.getATCount() != 0) return ValidationResult.FEATURE_NOT_YET_RELEASED; - // Check generator is allowed to forge this block - if (!isGeneratorValidToForge(parentBlock)) - return ValidationResult.GENERATOR_NOT_ACCEPTED; + // Check minter is allowed to mint this block + if (!isMinterValid(parentBlock)) + return ValidationResult.MINTER_NOT_ACCEPTED; // Online Accounts ValidationResult onlineAccountsResult = this.areOnlineAccountsValid(); @@ -1148,15 +1149,15 @@ public class Block { calcTransactionsSignature(); } - /** Returns whether block's generator is actually allowed to forge this block. */ - protected boolean isGeneratorValidToForge(Block parentBlock) throws DataException { - // Block's generator public key must be known proxy forging public key - ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.blockData.getGeneratorPublicKey()); - if (proxyForgerData == null) + /** Returns whether block's minter is actually allowed to mint this block. */ + protected boolean isMinterValid(Block parentBlock) throws DataException { + // Qortal: block's minter public key must be known reward-share public key + RewardShareData rewardShareData = this.repository.getAccountRepository().getRewardShare(this.blockData.getMinterPublicKey()); + if (rewardShareData == null) return false; - Account forger = new PublicKeyAccount(this.repository, proxyForgerData.getForgerPublicKey()); - return forger.canForge(); + Account mintingAccount = new PublicKeyAccount(this.repository, rewardShareData.getMinterPublicKey()); + return mintingAccount.canMint(); } /** @@ -1184,7 +1185,7 @@ public class Block { processGroupApprovalTransactions(); if (this.blockData.getHeight() > 1) - // Give transaction fees to generator/proxy + // Give transaction fees to minter/reward-share account(s) rewardTransactionFees(); // Process AT fees and save AT states into repository @@ -1204,8 +1205,8 @@ public class Block { } protected void increaseAccountLevels() throws DataException { - // We need to do this for both forgers and recipients - this.increaseAccountLevels(expandedAccount -> expandedAccount.isForgerFounder, expandedAccount -> expandedAccount.forgerAccountData); + // We need to do this for both minters and recipients + this.increaseAccountLevels(expandedAccount -> expandedAccount.isMinterFounder, expandedAccount -> expandedAccount.mintingAccountData); this.increaseAccountLevels(expandedAccount -> expandedAccount.isRecipientFounder, expandedAccount -> expandedAccount.recipientAccountData); } @@ -1214,19 +1215,19 @@ public class Block { final List expandedAccounts = this.getExpandedAccounts(); final boolean isProcessingRecipients = getAccountData.apply(expandedAccounts.get(0)) == expandedAccounts.get(0).recipientAccountData; - // Increase blocks generated count for all accounts + // Increase blocks-minted count for all accounts for (int a = 0; a < expandedAccounts.size(); ++a) { ExpandedAccount expandedAccount = expandedAccounts.get(a); - // Don't increase twice if recipient is also forger. - if (isProcessingRecipients && expandedAccount.isRecipientAlsoForger) + // Don't increase twice if recipient is also minter. + if (isProcessingRecipients && expandedAccount.isRecipientAlsoMinter) continue; AccountData accountData = getAccountData.apply(expandedAccount); - accountData.setBlocksGenerated(accountData.getBlocksGenerated() + 1); - repository.getAccountRepository().setBlocksGenerated(accountData); - LOGGER.trace(() -> String.format("Block generator %s up to %d generated block%s", accountData.getAddress(), accountData.getBlocksGenerated(), (accountData.getBlocksGenerated() != 1 ? "s" : ""))); + accountData.setBlocksMinted(accountData.getBlocksMinted() + 1); + repository.getAccountRepository().setMintedBlockCount(accountData); + LOGGER.trace(() -> String.format("Block minted %s up to %d minted block%s", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""))); } // We are only interested in accounts that are NOT founders and NOT already highest level @@ -1237,15 +1238,15 @@ public class Block { ExpandedAccount expandedAccount = candidateAccounts.get(c); final AccountData accountData = getAccountData.apply(expandedAccount); - final int effectiveBlocksGenerated = cumulativeBlocksByLevel.get(accountData.getInitialLevel()) + accountData.getBlocksGenerated(); + final int effectiveBlocksMinted = cumulativeBlocksByLevel.get(accountData.getInitialLevel()) + accountData.getBlocksMinted(); for (int newLevel = maximumLevel; newLevel > 0; --newLevel) - if (effectiveBlocksGenerated >= cumulativeBlocksByLevel.get(newLevel)) { + if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) { if (newLevel > accountData.getLevel()) { // Account has increased in level! accountData.setLevel(newLevel); repository.getAccountRepository().setLevel(accountData); - LOGGER.trace(() -> String.format("Block generator %s bumped to level %d", accountData.getAddress(), accountData.getLevel())); + LOGGER.trace(() -> String.format("Block minter %s bumped to level %d", accountData.getAddress(), accountData.getLevel())); } break; @@ -1393,7 +1394,7 @@ public class Block { */ public void orphan() throws DataException { if (this.blockData.getHeight() > 1) - // Deduct any transaction fees from generator/proxy + // Deduct any transaction fees from minter/reward-share account(s) deductTransactionFees(); // Orphan, and unlink, transactions from this block @@ -1512,8 +1513,8 @@ public class Block { } protected void decreaseAccountLevels() throws DataException { - // We need to do this for both forgers and recipients - this.decreaseAccountLevels(expandedAccount -> expandedAccount.isForgerFounder, expandedAccount -> expandedAccount.forgerAccountData); + // We need to do this for both minters and recipients + this.decreaseAccountLevels(expandedAccount -> expandedAccount.isMinterFounder, expandedAccount -> expandedAccount.mintingAccountData); this.decreaseAccountLevels(expandedAccount -> expandedAccount.isRecipientFounder, expandedAccount -> expandedAccount.recipientAccountData); } @@ -1522,19 +1523,19 @@ public class Block { final List expandedAccounts = this.getExpandedAccounts(); final boolean isProcessingRecipients = getAccountData.apply(expandedAccounts.get(0)) == expandedAccounts.get(0).recipientAccountData; - // Decrease blocks generated count for all accounts + // Decrease blocks minted count for all accounts for (int a = 0; a < expandedAccounts.size(); ++a) { ExpandedAccount expandedAccount = expandedAccounts.get(a); - // Don't decrease twice if recipient is also forger. - if (isProcessingRecipients && expandedAccount.isRecipientAlsoForger) + // Don't decrease twice if recipient is also minter. + if (isProcessingRecipients && expandedAccount.isRecipientAlsoMinter) continue; AccountData accountData = getAccountData.apply(expandedAccount); - accountData.setBlocksGenerated(accountData.getBlocksGenerated() - 1); - repository.getAccountRepository().setBlocksGenerated(accountData); - LOGGER.trace(() -> String.format("Block generator %s down to %d generated block%s", accountData.getAddress(), accountData.getBlocksGenerated(), (accountData.getBlocksGenerated() != 1 ? "s" : ""))); + accountData.setBlocksMinted(accountData.getBlocksMinted() - 1); + repository.getAccountRepository().setMintedBlockCount(accountData); + LOGGER.trace(() -> String.format("Block minter %s down to %d minted block%s", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""))); } // We are only interested in accounts that are NOT founders and NOT already lowest level @@ -1545,15 +1546,15 @@ public class Block { ExpandedAccount expandedAccount = candidateAccounts.get(c); final AccountData accountData = getAccountData.apply(expandedAccount); - final int effectiveBlocksGenerated = cumulativeBlocksByLevel.get(accountData.getInitialLevel()) + accountData.getBlocksGenerated(); + final int effectiveBlocksMinted = cumulativeBlocksByLevel.get(accountData.getInitialLevel()) + accountData.getBlocksMinted(); for (int newLevel = maximumLevel; newLevel > 0; --newLevel) - if (effectiveBlocksGenerated >= cumulativeBlocksByLevel.get(newLevel)) { + if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) { if (newLevel < accountData.getLevel()) { // Account has decreased in level! accountData.setLevel(newLevel); repository.getAccountRepository().setLevel(accountData); - LOGGER.trace(() -> String.format("Block generator %s reduced to level %d", accountData.getAddress(), accountData.getLevel())); + LOGGER.trace(() -> String.format("Block minter %s reduced to level %d", accountData.getAddress(), accountData.getLevel())); } break; @@ -1574,7 +1575,7 @@ public class Block { LOGGER.trace(() -> String.format("Bin %d share of %s: %s", binIndex, totalAmount.toPlainString(), binAmount.toPlainString())); // Spread across all accounts in bin - List binnedAccounts = expandedAccounts.stream().filter(accountInfo -> !accountInfo.isForgerFounder && accountInfo.shareBin == binIndex).collect(Collectors.toList()); + List binnedAccounts = expandedAccounts.stream().filter(accountInfo -> !accountInfo.isMinterFounder && accountInfo.shareBin == binIndex).collect(Collectors.toList()); if (binnedAccounts.isEmpty()) continue; @@ -1596,11 +1597,11 @@ public class Block { BigDecimal totalQoraHeld = BigDecimal.ZERO; for (int i = 0; i < expandedAccounts.size(); ++i) { ExpandedAccount expandedAccount = expandedAccounts.get(i); - if (expandedAccount.forgerQoraAmount == null) + if (expandedAccount.minterQoraAmount == null) continue; qoraHolderAccounts.add(expandedAccount); - totalQoraHeld = totalQoraHeld.add(expandedAccount.forgerQoraAmount); + totalQoraHeld = totalQoraHeld.add(expandedAccount.minterQoraAmount); } final BigDecimal finalTotalQoraHeld = totalQoraHeld; @@ -1608,9 +1609,9 @@ public class Block { for (int h = 0; h < qoraHolderAccounts.size(); ++h) { ExpandedAccount expandedAccount = qoraHolderAccounts.get(h); - final BigDecimal holderAmount = qoraHoldersAmount.multiply(totalQoraHeld).divide(expandedAccount.forgerQoraAmount, RoundingMode.DOWN); - LOGGER.trace(() -> String.format("Forger account %s has %s / %s QORA so share: %s", - expandedAccount.forgerAccount.getAddress(), expandedAccount.forgerQoraAmount, finalTotalQoraHeld, holderAmount.toPlainString())); + final BigDecimal holderAmount = qoraHoldersAmount.multiply(totalQoraHeld).divide(expandedAccount.minterQoraAmount, RoundingMode.DOWN); + LOGGER.trace(() -> String.format("Minter account %s has %s / %s QORA so share: %s", + expandedAccount.mintingAccount.getAddress(), expandedAccount.minterQoraAmount, finalTotalQoraHeld, holderAmount.toPlainString())); expandedAccount.distribute(holderAmount); sharedAmount = sharedAmount.add(holderAmount); @@ -1620,7 +1621,7 @@ public class Block { BigDecimal foundersAmount = totalAmount.subtract(sharedAmount); LOGGER.debug(String.format("Shared %s of %s, remaining %s to founders", sharedAmount.toPlainString(), totalAmount.toPlainString(), foundersAmount.toPlainString())); - List founderAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isForgerFounder).collect(Collectors.toList()); + List founderAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isMinterFounder).collect(Collectors.toList()); if (founderAccounts.isEmpty()) return; diff --git a/src/main/java/org/qora/block/BlockChain.java b/src/main/java/org/qora/block/BlockChain.java index d27a0b6e..c704a83c 100644 --- a/src/main/java/org/qora/block/BlockChain.java +++ b/src/main/java/org/qora/block/BlockChain.java @@ -61,8 +61,6 @@ public class BlockChain { private BigDecimal maxBytesPerUnitFee; private BigDecimal minFeePerByte; - /** Number of blocks between recalculating block's generating balance. */ - private int blockDifficultyInterval; /** Maximum acceptable timestamp disagreement offset in milliseconds. */ private long blockTimestampMargin; /** Maximum block size, in bytes. */ @@ -113,15 +111,23 @@ public class BlockChain { BigDecimal qoraHoldersShare; /** - * Number of generated blocks required to reach next level from previous. + * Number of minted blocks required to reach next level from previous. *

* Use account's current level as index.
* If account's level isn't valid as an index, then account's level is at maximum. + *

+ * Example: if blocksNeededByLevel[3] is 200,
+ * then level 3 accounts need to mint 200 blocks to reach level 4. */ List blocksNeededByLevel; /** - * Cumulative number of generated blocks required to reach level from scratch. + * Cumulative number of minted blocks required to reach next level from scratch. + *

+ * Use target level as index. cumulativeBlocksByLevel[0] should be 0. + *

+ * Example; if cumulativeBlocksByLevel[2] is 1800,
+ * the a new account will need to mint 1800 blocks to reach level 2. *

* Generated just after blockchain config is parsed and validated. *

@@ -138,7 +144,9 @@ public class BlockChain { } List blockTimingsByHeight; - private int maxProxyRelationships; + private int minAccountLevelToMint = 1; + private int minAccountLevelToRewardShare; + private int maxRewardSharesPerMintingAccount; /** Minimum time to retain online account signatures (ms) for block validity checks. */ private long onlineAccountSignaturesMinLifetime; @@ -263,10 +271,6 @@ public class BlockChain { return this.transactionExpiryPeriod; } - public int getBlockDifficultyInterval() { - return this.blockDifficultyInterval; - } - public long getBlockTimestampMargin() { return this.blockTimestampMargin; } @@ -308,8 +312,16 @@ public class BlockChain { return this.qoraHoldersShare; } - public int getMaxProxyRelationships() { - return this.maxProxyRelationships; + public int getMinAccountLevelToMint() { + return this.minAccountLevelToMint; + } + + public int getMinAccountLevelToRewardShare() { + return this.minAccountLevelToRewardShare; + } + + public int getMaxRewardSharesPerMintingAccount() { + return this.maxRewardSharesPerMintingAccount; } public long getOnlineAccountSignaturesMinLifetime() { @@ -406,6 +418,10 @@ public class BlockChain { if (this.maxBlockSize <= 0) Settings.throwValidationError("Invalid \"maxBlockSize\" in blockchain config"); + if (this.minAccountLevelToRewardShare <= 0) + Settings.throwValidationError("Invalid/missing \"minAccountLevelToRewardShare\" in blockchain config"); + + if (this.featureTriggers == null) Settings.throwValidationError("No \"featureTriggers\" entry found in blockchain config"); diff --git a/src/main/java/org/qora/block/BlockGenerator.java b/src/main/java/org/qora/block/BlockMinter.java similarity index 75% rename from src/main/java/org/qora/block/BlockGenerator.java rename to src/main/java/org/qora/block/BlockMinter.java index cbf3431a..6cbeaedc 100644 --- a/src/main/java/org/qora/block/BlockGenerator.java +++ b/src/main/java/org/qora/block/BlockMinter.java @@ -14,8 +14,8 @@ import org.qora.account.PrivateKeyAccount; import org.qora.account.PublicKeyAccount; import org.qora.block.Block.ValidationResult; import org.qora.controller.Controller; -import org.qora.data.account.ForgingAccountData; -import org.qora.data.account.ProxyForgerData; +import org.qora.data.account.MintingAccountData; +import org.qora.data.account.RewardShareData; import org.qora.data.block.BlockData; import org.qora.data.transaction.TransactionData; import org.qora.network.Network; @@ -29,28 +29,26 @@ import org.qora.transaction.Transaction; import org.qora.utils.Base58; import org.qora.utils.NTP; -// Forging new blocks +// Minting new blocks -// How is the private key going to be supplied? - -public class BlockGenerator extends Thread { +public class BlockMinter extends Thread { // Properties private boolean running; // Other properties - private static final Logger LOGGER = LogManager.getLogger(BlockGenerator.class); + private static final Logger LOGGER = LogManager.getLogger(BlockMinter.class); // Constructors - public BlockGenerator() { + public BlockMinter() { this.running = true; } // Main thread loop @Override public void run() { - Thread.currentThread().setName("BlockGenerator"); + Thread.currentThread().setName("BlockMinter"); try (final Repository repository = RepositoryManager.getRepository()) { if (Settings.getInstance().getWipeUnconfirmedOnStart()) { @@ -71,19 +69,19 @@ public class BlockGenerator extends Thread { List newBlocks = new ArrayList<>(); - // Flags that allow us to track whether generating is possible changes, + // Flags for tracking change in whether minting is possible, // so we can notify Controller, and further update SysTray, etc. - boolean isGenerationPossible = false; - boolean wasGenerationPossible = isGenerationPossible; + boolean isMintingPossible = false; + boolean wasMintingPossible = isMintingPossible; while (running) { // Sleep for a while try { repository.discardChanges(); // Free repository locks, if any - if (isGenerationPossible != wasGenerationPossible) - Controller.getInstance().onGenerationPossibleChange(isGenerationPossible); + if (isMintingPossible != wasMintingPossible) + Controller.getInstance().onMintingPossibleChange(isMintingPossible); - wasGenerationPossible = isGenerationPossible; + wasMintingPossible = isMintingPossible; Thread.sleep(1000); } catch (InterruptedException e) { @@ -91,7 +89,7 @@ public class BlockGenerator extends Thread { return; } - isGenerationPossible = false; + isMintingPossible = false; final Long now = NTP.getTime(); if (now == null) @@ -105,9 +103,9 @@ public class BlockGenerator extends Thread { if (Controller.getInstance().getOnlineAccounts().isEmpty()) continue; - List forgingAccountsData = repository.getAccountRepository().getForgingAccounts(); - // No forging accounts? - if (forgingAccountsData.isEmpty()) + List mintingAccountsData = repository.getAccountRepository().getMintingAccounts(); + // No minting accounts? + if (mintingAccountsData.isEmpty()) continue; List peers = Network.getInstance().getUniqueHandshakedPeers(); @@ -116,7 +114,7 @@ public class BlockGenerator extends Thread { // Disregard peers that have "misbehaved" recently peers.removeIf(Controller.hasMisbehaved); - // Don't generate if we don't have enough connected peers as where would the transactions/consensus come from? + // Don't mint if we don't have enough connected peers as where would the transactions/consensus come from? if (peers.size() < Settings.getInstance().getMinBlockchainPeers()) continue; @@ -124,13 +122,13 @@ public class BlockGenerator extends Thread { peers.removeIf(Controller.hasNoRecentBlock); // If we have any peers with a recent block, but our latest block isn't recent - // then we need to synchronize instead of generating. + // then we need to synchronize instead of minting. if (!peers.isEmpty() && lastBlockData.getTimestamp() < minLatestBlockTimestamp) continue; // There are no peers with a recent block and/or our latest block is recent - // so go ahead and generate a block if possible. - isGenerationPossible = true; + // so go ahead and mint a block if possible. + isMintingPossible = true; // Check blockchain hasn't changed if (previousBlock == null || !Arrays.equals(previousBlock.getSignature(), lastBlockData.getSignature())) { @@ -139,20 +137,20 @@ public class BlockGenerator extends Thread { } // Do we need to build any potential new blocks? - List forgingAccounts = forgingAccountsData.stream().map(accountData -> new PrivateKeyAccount(repository, accountData.getSeed())).collect(Collectors.toList()); + List mintingAccounts = mintingAccountsData.stream().map(accountData -> new PrivateKeyAccount(repository, accountData.getPrivateKey())).collect(Collectors.toList()); // Discard accounts we have blocks for - forgingAccounts.removeIf(account -> newBlocks.stream().anyMatch(newBlock -> newBlock.getGenerator().getAddress().equals(account.getAddress()))); + mintingAccounts.removeIf(account -> newBlocks.stream().anyMatch(newBlock -> newBlock.getMinter().getAddress().equals(account.getAddress()))); - for (PrivateKeyAccount generator : forgingAccounts) { + for (PrivateKeyAccount mintingAccount : mintingAccounts) { // First block does the AT heavy-lifting if (newBlocks.isEmpty()) { - Block newBlock = new Block(repository, previousBlock.getBlockData(), generator); + Block newBlock = new Block(repository, previousBlock.getBlockData(), mintingAccount); newBlocks.add(newBlock); } else { - // The blocks for other generators require less effort... + // The blocks for other minters require less effort... Block newBlock = newBlocks.get(0); - newBlocks.add(newBlock.regenerate(generator)); + newBlocks.add(newBlock.newMinter(mintingAccount)); } } @@ -165,7 +163,7 @@ public class BlockGenerator extends Thread { if (!blockchainLock.tryLock()) continue; - boolean newBlockGenerated = false; + boolean newBlockMinted = false; try { // Clear repository's "in transaction" state so we don't cause a repository deadlock @@ -188,7 +186,8 @@ public class BlockGenerator extends Thread { if (goodBlocks.isEmpty()) continue; - // Pick random generator + // Pick random block + // TODO/XXX - shouldn't this pick our BEST block instead? int winningIndex = new Random().nextInt(goodBlocks.size()); Block newBlock = goodBlocks.get(winningIndex); @@ -205,7 +204,7 @@ public class BlockGenerator extends Thread { ValidationResult validationResult = newBlock.isValid(); if (validationResult != ValidationResult.OK) { // No longer valid? Report and discard - LOGGER.error(String.format("Valid, generated block now invalid '%s' after adding unconfirmed transactions?", validationResult.name())); + LOGGER.error(String.format("To-be-minted block now invalid '%s' after adding unconfirmed transactions?", validationResult.name())); // Rebuild block candidates, just to be sure newBlocks.clear(); @@ -216,43 +215,43 @@ public class BlockGenerator extends Thread { try { newBlock.process(); - LOGGER.info(String.format("Generated new block: %d", newBlock.getBlockData().getHeight())); + LOGGER.info(String.format("Minted new block: %d", newBlock.getBlockData().getHeight())); repository.saveChanges(); - ProxyForgerData proxyForgerData = repository.getAccountRepository().getProxyForgeData(newBlock.getBlockData().getGeneratorPublicKey()); + RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(newBlock.getBlockData().getMinterPublicKey()); - if (proxyForgerData != null) { - PublicKeyAccount forger = new PublicKeyAccount(repository, proxyForgerData.getForgerPublicKey()); - LOGGER.info(String.format("Generated block %d, sig %.8s by %s on behalf of %s", + if (rewardShareData != null) { + PublicKeyAccount mintingAccount = new PublicKeyAccount(repository, rewardShareData.getMinterPublicKey()); + LOGGER.info(String.format("Minted block %d, sig %.8s by %s on behalf of %s", newBlock.getBlockData().getHeight(), Base58.encode(newBlock.getBlockData().getSignature()), - forger.getAddress(), - proxyForgerData.getRecipient())); + mintingAccount.getAddress(), + rewardShareData.getRecipient())); } else { - LOGGER.info(String.format("Generated block %d, sig %.8s by %s", + LOGGER.info(String.format("Minted block %d, sig %.8s by %s", newBlock.getBlockData().getHeight(), Base58.encode(newBlock.getBlockData().getSignature()), - newBlock.getGenerator().getAddress())); + newBlock.getMinter().getAddress())); } repository.saveChanges(); // Notify controller - newBlockGenerated = true; + newBlockMinted = true; } catch (DataException e) { // Unable to process block - report and discard - LOGGER.error("Unable to process newly generated block?", e); + LOGGER.error("Unable to process newly minted block?", e); newBlocks.clear(); } } finally { blockchainLock.unlock(); } - if (newBlockGenerated) - Controller.getInstance().onGeneratedBlock(); + if (newBlockMinted) + Controller.getInstance().onBlockMinted(); } } catch (DataException e) { - LOGGER.warn("Repository issue while running block generator", e); + LOGGER.warn("Repository issue while running block minter", e); } } @@ -316,7 +315,7 @@ public class BlockGenerator extends Thread { // If newBlock is no longer valid then we can't use transaction ValidationResult validationResult = newBlock.isValid(); if (validationResult != ValidationResult.OK) { - LOGGER.debug(() -> String.format("Skipping invalid transaction %s during block generation", Base58.encode(transactionData.getSignature()))); + LOGGER.debug(() -> String.format("Skipping invalid transaction %s during block minting", Base58.encode(transactionData.getSignature()))); newBlock.deleteTransaction(transactionData); } } @@ -328,15 +327,18 @@ public class BlockGenerator extends Thread { this.interrupt(); } - public static void generateTestingBlock(Repository repository, PrivateKeyAccount generator) throws DataException { + public static void mintTestingBlock(Repository repository, PrivateKeyAccount mintingAccount) throws DataException { if (!BlockChain.getInstance().isTestChain()) { - LOGGER.warn("Ignoring attempt to generate testing block for non-test chain!"); + LOGGER.warn("Ignoring attempt to mint testing block for non-test chain!"); return; } + // Ensure mintingAccount is 'online' so blocks can be minted + Controller.getInstance().ensureTestingAccountOnline(mintingAccount); + BlockData previousBlockData = repository.getBlockRepository().getLastBlock(); - Block newBlock = new Block(repository, previousBlockData, generator); + Block newBlock = new Block(repository, previousBlockData, mintingAccount); // Make sure we're the only thread modifying the blockchain ReentrantLock blockchainLock = Controller.getInstance().getBlockchainLock(); @@ -354,8 +356,7 @@ public class BlockGenerator extends Thread { // Is newBlock still valid? ValidationResult validationResult = newBlock.isValid(); if (validationResult != ValidationResult.OK) - throw new IllegalStateException( - "Valid, generated block now invalid '" + validationResult.name() + "' after adding unconfirmed transactions?"); + throw new IllegalStateException(String.format("To-be-minted test block now invalid '%s' after adding unconfirmed transactions?", validationResult.name())); // Add to blockchain newBlock.process(); diff --git a/src/main/java/org/qora/block/GenesisBlock.java b/src/main/java/org/qora/block/GenesisBlock.java index 2fdbb47e..8912121a 100644 --- a/src/main/java/org/qora/block/GenesisBlock.java +++ b/src/main/java/org/qora/block/GenesisBlock.java @@ -125,16 +125,16 @@ public class GenesisBlock extends Block { byte[] reference = GENESIS_REFERENCE; int transactionCount = transactionsData.size(); BigDecimal totalFees = BigDecimal.ZERO.setScale(8); - byte[] generatorPublicKey = GenesisAccount.PUBLIC_KEY; - byte[] bytesForSignature = getBytesForSignature(info.version, reference, generatorPublicKey); - byte[] generatorSignature = calcSignature(bytesForSignature); - byte[] transactionsSignature = generatorSignature; + byte[] minterPublicKey = GenesisAccount.PUBLIC_KEY; + byte[] bytesForSignature = getBytesForSignature(info.version, reference, minterPublicKey); + byte[] minterSignature = calcSignature(bytesForSignature); + byte[] transactionsSignature = minterSignature; int height = 1; int atCount = 0; BigDecimal atFees = BigDecimal.ZERO.setScale(8); genesisBlockData = new BlockData(info.version, reference, transactionCount, totalFees, transactionsSignature, height, info.timestamp, - generatorPublicKey, generatorSignature, atCount, atFees); + minterPublicKey, minterSignature, atCount, atFees); } // More information @@ -146,7 +146,7 @@ public class GenesisBlock extends Block { byte[] signature = calcSignature(blockData); // Validate block signature - if (!Arrays.equals(signature, blockData.getGeneratorSignature())) + if (!Arrays.equals(signature, blockData.getMinterSignature())) return false; // Validate transactions signature @@ -169,7 +169,7 @@ public class GenesisBlock extends Block { } /** - * Refuse to calculate genesis block's generator signature! + * Refuse to calculate genesis block's minter signature! *

* This is not possible as there is no private key for the genesis account and so no way to sign data. *

@@ -178,7 +178,7 @@ public class GenesisBlock extends Block { * @throws IllegalStateException */ @Override - public void calcGeneratorSignature() { + public void calcMinterSignature() { throw new IllegalStateException("There is no private key for genesis account"); } @@ -197,7 +197,7 @@ public class GenesisBlock extends Block { } /** - * Generate genesis block generator/transactions signature. + * Generate genesis block minter/transactions signature. *

* This is handled differently as there is no private key for the genesis account and so no way to sign data. *

@@ -210,7 +210,7 @@ public class GenesisBlock extends Block { return Bytes.concat(digest, digest); } - private static byte[] getBytesForSignature(int version, byte[] reference, byte[] generatorPublicKey) { + private static byte[] getBytesForSignature(int version, byte[] reference, byte[] minterPublicKey) { try { // Passing expected size to ByteArrayOutputStream avoids reallocation when adding more bytes than default 32. // See below for explanation of some of the values used to calculated expected size. @@ -230,7 +230,7 @@ public class GenesisBlock extends Block { bytes.write(Bytes.ensureCapacity(reference, 64, 0)); // NOTE: Genesis account's public key is only 8 bytes, not the usual 32, so we have to pad. - bytes.write(Bytes.ensureCapacity(generatorPublicKey, 32, 0)); + bytes.write(Bytes.ensureCapacity(minterPublicKey, 32, 0)); return bytes.toByteArray(); } catch (IOException e) { @@ -240,7 +240,7 @@ public class GenesisBlock extends Block { /** Convenience method for calculating genesis block signatures from block data */ private static byte[] calcSignature(BlockData blockData) { - byte[] bytes = getBytesForSignature(blockData.getVersion(), blockData.getReference(), blockData.getGeneratorPublicKey()); + byte[] bytes = getBytesForSignature(blockData.getVersion(), blockData.getReference(), blockData.getMinterPublicKey()); return calcSignature(bytes); } @@ -249,7 +249,7 @@ public class GenesisBlock extends Block { byte[] signature = calcSignature(this.blockData); // Validate block signature - if (!Arrays.equals(signature, this.blockData.getGeneratorSignature())) + if (!Arrays.equals(signature, this.blockData.getMinterSignature())) return false; // Validate transactions signature diff --git a/src/main/java/org/qora/controller/Controller.java b/src/main/java/org/qora/controller/Controller.java index 3c9669e5..034110c3 100644 --- a/src/main/java/org/qora/controller/Controller.java +++ b/src/main/java/org/qora/controller/Controller.java @@ -31,10 +31,10 @@ import org.qora.api.ApiService; import org.qora.block.Block; import org.qora.block.BlockChain; import org.qora.block.BlockChain.BlockTimingByHeight; -import org.qora.block.BlockGenerator; +import org.qora.block.BlockMinter; import org.qora.controller.Synchronizer.SynchronizationResult; import org.qora.crypto.Crypto; -import org.qora.data.account.ForgingAccountData; +import org.qora.data.account.MintingAccountData; import org.qora.data.block.BlockData; import org.qora.data.block.BlockSummaryData; import org.qora.data.network.OnlineAccountData; @@ -114,7 +114,7 @@ public class Controller extends Thread { private static final long LAST_SEEN_EXPIRY_PERIOD = (ONLINE_TIMESTAMP_MODULUS * 2) + (1 * 60 * 1000L); private static volatile boolean isStopping = false; - private static BlockGenerator blockGenerator = null; + private static BlockMinter blockMinter = null; private static volatile boolean requestSync = false; private static volatile boolean requestSysTrayUpdate = false; private static Controller instance; @@ -129,8 +129,8 @@ public class Controller extends Thread { private long deleteExpiredTimestamp = startTime + DELETE_EXPIRED_INTERVAL; // ms private long onlineAccountsTasksTimestamp = startTime + ONLINE_ACCOUNTS_TASKS_INTERVAL; // ms - /** Whether we can generate new blocks, as reported by BlockGenerator. */ - private volatile boolean isGenerationPossible = false; + /** Whether we can mint new blocks, as reported by BlockMinter. */ + private volatile boolean isMintingPossible = false; /** Latest block signatures from other peers that we know are on inferior chains. */ List inferiorChainSignatures = new ArrayList<>(); @@ -154,7 +154,7 @@ public class Controller extends Thread { */ private Map> arbitraryDataRequests = Collections.synchronizedMap(new HashMap<>()); - /** Lock for only allowing one blockchain-modifying codepath at a time. e.g. synchronization or newly generated block. */ + /** Lock for only allowing one blockchain-modifying codepath at a time. e.g. synchronization or newly minted block. */ private final ReentrantLock blockchainLock = new ReentrantLock(); /** Cache of 'online accounts' */ @@ -300,9 +300,9 @@ public class Controller extends Thread { } }); - LOGGER.info("Starting block generator"); - blockGenerator = new BlockGenerator(); - blockGenerator.start(); + LOGGER.info("Starting block minter"); + blockMinter = new BlockMinter(); + blockMinter.start(); // Arbitrary transaction data manager // LOGGER.info("Starting arbitrary-transaction data manager"); @@ -566,9 +566,9 @@ public class Controller extends Thread { String connectionsText = Translator.INSTANCE.translate("SysTray", numberOfPeers != 1 ? "CONNECTIONS" : "CONNECTION"); String heightText = Translator.INSTANCE.translate("SysTray", "BLOCK_HEIGHT"); - String generatingText = Translator.INSTANCE.translate("SysTray", isGenerationPossible ? "GENERATING_ENABLED" : "GENERATING_DISABLED"); + String mintingText = Translator.INSTANCE.translate("SysTray", isMintingPossible ? "MINTING_ENABLED" : "MINTING_DISABLED"); - String tooltip = String.format("%s - %d %s - %s %d", generatingText, numberOfPeers, connectionsText, heightText, height); + String tooltip = String.format("%s - %d %s - %s %d", mintingText, numberOfPeers, connectionsText, heightText, height); SysTray.getInstance().setToolTipText(tooltip); } @@ -616,11 +616,11 @@ public class Controller extends Thread { // LOGGER.info("Shutting down arbitrary-transaction data manager"); // ArbitraryDataManager.getInstance().shutdown(); - if (blockGenerator != null) { - LOGGER.info("Shutting down block generator"); - blockGenerator.shutdown(); + if (blockMinter != null) { + LOGGER.info("Shutting down block minter"); + blockMinter.shutdown(); try { - blockGenerator.join(); + blockMinter.join(); } catch (InterruptedException e) { // We were interrupted while waiting for thread to join } @@ -673,12 +673,12 @@ public class Controller extends Thread { network.broadcast(network::buildGetUnconfirmedTransactionsMessage); } - public void onGenerationPossibleChange(boolean isGenerationPossible) { - this.isGenerationPossible = isGenerationPossible; + public void onMintingPossibleChange(boolean isMintingPossible) { + this.isMintingPossible = isMintingPossible; requestSysTrayUpdate = true; } - public void onGeneratedBlock() { + public void onBlockMinted() { // Broadcast our new height info BlockData latestBlockData; @@ -686,7 +686,7 @@ public class Controller extends Thread { latestBlockData = repository.getBlockRepository().getLastBlock(); this.setChainTip(latestBlockData); } catch (DataException e) { - LOGGER.warn(String.format("Repository issue when trying to fetch post-generation chain tip: %s", e.getMessage())); + LOGGER.warn(String.format("Repository issue when trying to fetch post-mint chain tip: %s", e.getMessage())); return; } @@ -768,7 +768,7 @@ public class Controller extends Thread { continue; // Update peer chain tip data - PeerChainTipData newChainTipData = new PeerChainTipData(blockData.getHeight(), blockData.getSignature(), blockData.getTimestamp(), blockData.getGeneratorPublicKey()); + PeerChainTipData newChainTipData = new PeerChainTipData(blockData.getHeight(), blockData.getSignature(), blockData.getTimestamp(), blockData.getMinterPublicKey()); connectedPeer.setChainTipData(newChainTipData); } @@ -817,7 +817,7 @@ public class Controller extends Thread { continue; // Update peer chain tip data - PeerChainTipData newChainTipData = new PeerChainTipData(heightV2Message.getHeight(), heightV2Message.getSignature(), heightV2Message.getTimestamp(), heightV2Message.getGenerator()); + PeerChainTipData newChainTipData = new PeerChainTipData(heightV2Message.getHeight(), heightV2Message.getSignature(), heightV2Message.getTimestamp(), heightV2Message.getMinterPublicKey()); connectedPeer.setChainTipData(newChainTipData); } @@ -1280,6 +1280,31 @@ public class Controller extends Thread { } } + public void ensureTestingAccountOnline(PrivateKeyAccount mintingAccount) { + if (!BlockChain.getInstance().isTestChain()) { + LOGGER.warn("Ignoring attempt to ensure test account is online for non-test chain!"); + return; + } + + final Long now = NTP.getTime(); + if (now == null) + return; + + // Check mintingAccount is actually reward-share? + + // Add reward-share & timestamp to online accounts + final long onlineAccountsTimestamp = Controller.toOnlineAccountTimestamp(now); + byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp); + + byte[] signature = mintingAccount.sign(timestampBytes); + byte[] publicKey = mintingAccount.getPublicKey(); + + OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey); + synchronized (this.onlineAccounts) { + this.onlineAccounts.add(ourOnlineAccountData); + } + } + private void performOnlineAccountsTasks() { final Long now = NTP.getTime(); if (now == null) @@ -1324,24 +1349,24 @@ public class Controller extends Thread { if (now == null) return; - List forgingAccounts; + List mintingAccounts; try (final Repository repository = RepositoryManager.getRepository()) { - forgingAccounts = repository.getAccountRepository().getForgingAccounts(); + mintingAccounts = repository.getAccountRepository().getMintingAccounts(); // We have no accounts, but don't reset timestamp - if (forgingAccounts.isEmpty()) + if (mintingAccounts.isEmpty()) return; - // Only proxy forging accounts allowed - Iterator iterator = forgingAccounts.iterator(); + // Only reward-share accounts allowed + Iterator iterator = mintingAccounts.iterator(); while (iterator.hasNext()) { - ForgingAccountData forgingAccountData = iterator.next(); + MintingAccountData mintingAccountData = iterator.next(); - if (!repository.getAccountRepository().isProxyPublicKey(forgingAccountData.getPublicKey())) + if (!repository.getAccountRepository().isRewardSharePublicKey(mintingAccountData.getPublicKey())) iterator.remove(); } } catch (DataException e) { - LOGGER.warn(String.format("Repository issue trying to fetch forging accounts: %s", e.getMessage())); + LOGGER.warn(String.format("Repository issue trying to fetch minting accounts: %s", e.getMessage())); return; } @@ -1352,12 +1377,12 @@ public class Controller extends Thread { byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp); List ourOnlineAccounts = new ArrayList<>(); - FORGING_ACCOUNTS: - for (ForgingAccountData forgingAccountData : forgingAccounts) { - PrivateKeyAccount forgingAccount = new PrivateKeyAccount(null, forgingAccountData.getSeed()); + MINTING_ACCOUNTS: + for (MintingAccountData mintingAccountData : mintingAccounts) { + PrivateKeyAccount mintingAccount = new PrivateKeyAccount(null, mintingAccountData.getPrivateKey()); - byte[] signature = forgingAccount.sign(timestampBytes); - byte[] publicKey = forgingAccount.getPublicKey(); + byte[] signature = mintingAccount.sign(timestampBytes); + byte[] publicKey = mintingAccount.getPublicKey(); // Our account is online OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey); @@ -1367,9 +1392,9 @@ public class Controller extends Thread { OnlineAccountData existingOnlineAccountData = iterator.next(); if (Arrays.equals(existingOnlineAccountData.getPublicKey(), ourOnlineAccountData.getPublicKey())) { - // If our online account is already present, with same timestamp, then move on to next forgingAccount + // If our online account is already present, with same timestamp, then move on to next mintingAccount if (existingOnlineAccountData.getTimestamp() == onlineAccountsTimestamp) - continue FORGING_ACCOUNTS; + continue MINTING_ACCOUNTS; // If our online account is already present, but with older timestamp, then remove it iterator.remove(); @@ -1380,7 +1405,7 @@ public class Controller extends Thread { this.onlineAccounts.add(ourOnlineAccountData); } - LOGGER.trace(() -> String.format("Added our online account %s with timestamp %d", forgingAccount.getAddress(), onlineAccountsTimestamp)); + LOGGER.trace(() -> String.format("Added our online account %s with timestamp %d", mintingAccount.getAddress(), onlineAccountsTimestamp)); ourOnlineAccounts.add(ourOnlineAccountData); hasInfoChanged = true; } @@ -1398,6 +1423,7 @@ public class Controller extends Thread { return (timestamp / ONLINE_TIMESTAMP_MODULUS) * ONLINE_TIMESTAMP_MODULUS; } + /** Returns list of online accounts with timestamp recent enough to be considered currently online. */ public List getOnlineAccounts() { final long onlineTimestamp = Controller.toOnlineAccountTimestamp(NTP.getTime()); @@ -1513,7 +1539,7 @@ public class Controller extends Thread { // Disregard peers that don't have a recent block peers.removeIf(hasNoRecentBlock); - // Check we have enough peers to potentially synchronize/generate + // Check we have enough peers to potentially synchronize/mint if (peers.size() < Settings.getInstance().getMinBlockchainPeers()) return false; diff --git a/src/main/java/org/qora/crosschain/BTC.java b/src/main/java/org/qora/crosschain/BTC.java index 009f7c34..46c34431 100644 --- a/src/main/java/org/qora/crosschain/BTC.java +++ b/src/main/java/org/qora/crosschain/BTC.java @@ -158,7 +158,7 @@ public class BTC { checkpointsFileName = "checkpoints.txt"; } - directory = new File("Qora-BTC"); + directory = new File("Qortal-BTC"); if (!directory.exists()) directory.mkdirs(); @@ -191,7 +191,7 @@ public class BTC { } peerGroup = new PeerGroup(params, chain); - peerGroup.setUserAgent("qqq", "1.0"); + peerGroup.setUserAgent("qortal", "1.0"); peerGroup.addPeerDiscovery(new DnsDiscovery(params)); peerGroup.start(); } diff --git a/src/main/java/org/qora/crypto/CiyamMemoryPoW.java b/src/main/java/org/qora/crypto/CiyamMemoryPoW.java new file mode 100644 index 00000000..b0cf2458 --- /dev/null +++ b/src/main/java/org/qora/crypto/CiyamMemoryPoW.java @@ -0,0 +1,83 @@ +package org.qora.crypto; + +import com.google.common.primitives.Bytes; + +public class CiyamMemoryPoW { + + private static final int WORK_BUFFER_LENGTH = 4 * 1024 * 1024; + private static final int WORK_BUFFER_LENGTH_MASK = WORK_BUFFER_LENGTH - 1; + + private static final int HASH_LENGTH = 32; + private static final int HASH_LENGTH_MASK = HASH_LENGTH - 1; + + public static Integer compute(byte[] data, int start, int range, int difficulty) { + if (range < 1) + throw new IllegalArgumentException("range must be at least 1"); + + if (difficulty < 1) + throw new IllegalArgumentException("difficulty must be at least 1"); + + // Hash data with SHA256 + byte[] hash = Crypto.digest(data); + + assert hash.length == HASH_LENGTH; + + byte[] perturbedHash = new byte[HASH_LENGTH]; + byte[] workBuffer = new byte[WORK_BUFFER_LENGTH]; + byte[] bufferHash = new byte[HASH_LENGTH]; + + // For each nonce... + for (int nonce = start; nonce < start + range; ++nonce) { + // Perturb hash using nonce + int temp = nonce; + for (int hi = 0; hi < HASH_LENGTH; ++hi) { + perturbedHash[hi] = (byte) (hash[hi] ^ (temp & 0xff)); + temp >>>= 1; + } + + // Fill large working memory buffer using hash, further perturbing as we go + int wanderingBufferOffset = 0; + byte ch = 0; + + int hashOffset = 0; + + for (int workBufferOffset = 0; workBufferOffset < WORK_BUFFER_LENGTH; workBufferOffset += HASH_LENGTH) { + System.arraycopy(perturbedHash, 0, workBuffer, workBufferOffset, HASH_LENGTH); + + hashOffset = ++hashOffset & HASH_LENGTH_MASK; + + ch += perturbedHash[hashOffset]; + + for (byte hi = 0; hi < HASH_LENGTH; ++hi) { + byte hashByte = perturbedHash[hi]; + wanderingBufferOffset = (wanderingBufferOffset << 3) ^ (hashByte & 0xff); + + perturbedHash[hi] = (byte) (hashByte ^ (ch + hi)); + } + + workBuffer[wanderingBufferOffset & WORK_BUFFER_LENGTH_MASK] ^= 0xAA; + + // final int finalWanderingBufferOffset = wanderingBufferOffset & WORK_BUFFER_LENGTH_MASK; + // System.out.println(String.format("wanderingBufferOffset: 0x%08x / 0x%08x - %02d%%", finalWanderingBufferOffset, WORK_BUFFER_LENGTH, finalWanderingBufferOffset * 100 / WORK_BUFFER_LENGTH)); + } + + Bytes.reverse(workBuffer); + + // bufferHash = Crypto.digest(workBuffer); + System.arraycopy(workBuffer, 0, bufferHash, 0, HASH_LENGTH); + + int hi = 0; + for (hi = 0; hi < difficulty; ++hi) + if (bufferHash[hi] != 0) + break; + + if (hi == difficulty) + return nonce; + + Thread.yield(); + } + + return null; + } + +} diff --git a/src/main/java/org/qora/data/account/AccountData.java b/src/main/java/org/qora/data/account/AccountData.java index 6098b087..f1a53deb 100644 --- a/src/main/java/org/qora/data/account/AccountData.java +++ b/src/main/java/org/qora/data/account/AccountData.java @@ -17,7 +17,7 @@ public class AccountData { protected int flags; protected int initialLevel; protected int level; - protected int blocksGenerated; + protected int blocksMinted; // Constructors @@ -25,7 +25,7 @@ public class AccountData { protected AccountData() { } - public AccountData(String address, byte[] reference, byte[] publicKey, int defaultGroupId, int flags, int initialLevel, int level, int blocksGenerated) { + public AccountData(String address, byte[] reference, byte[] publicKey, int defaultGroupId, int flags, int initialLevel, int level, int blocksMinted) { this.address = address; this.reference = reference; this.publicKey = publicKey; @@ -33,7 +33,7 @@ public class AccountData { this.flags = flags; this.initialLevel = initialLevel; this.level = level; - this.blocksGenerated = blocksGenerated; + this.blocksMinted = blocksMinted; } public AccountData(String address) { @@ -94,12 +94,12 @@ public class AccountData { this.level = level; } - public int getBlocksGenerated() { - return this.blocksGenerated; + public int getBlocksMinted() { + return this.blocksMinted; } - public void setBlocksGenerated(int blocksGenerated) { - this.blocksGenerated = blocksGenerated; + public void setBlocksMinted(int blocksMinted) { + this.blocksMinted = blocksMinted; } // Comparison diff --git a/src/main/java/org/qora/data/account/ForgingAccountData.java b/src/main/java/org/qora/data/account/MintingAccountData.java similarity index 60% rename from src/main/java/org/qora/data/account/ForgingAccountData.java rename to src/main/java/org/qora/data/account/MintingAccountData.java index c0d84528..e9bfeb4f 100644 --- a/src/main/java/org/qora/data/account/ForgingAccountData.java +++ b/src/main/java/org/qora/data/account/MintingAccountData.java @@ -13,38 +13,38 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) -public class ForgingAccountData { +public class MintingAccountData { // Properties @Schema(hidden = true) @XmlTransient - protected byte[] seed; + protected byte[] privateKey; // Not always present - used by API if not null @XmlTransient @Schema(hidden = true) protected byte[] publicKey; - protected String proxiedBy; - protected String proxiedFor; + protected String mintingAccount; + protected String recipientAccount; protected String address; // Constructors // For JAXB - protected ForgingAccountData() { + protected MintingAccountData() { } - public ForgingAccountData(byte[] seed) { - this.seed = seed; - this.publicKey = new PrivateKeyAccount(null, seed).getPublicKey(); + public MintingAccountData(byte[] privateKey) { + this.privateKey = privateKey; + this.publicKey = PrivateKeyAccount.toPublicKey(privateKey); } - public ForgingAccountData(byte[] seed, ProxyForgerData proxyForgerData) { - this(seed); + public MintingAccountData(byte[] privateKey, RewardShareData rewardShareData) { + this(privateKey); - if (proxyForgerData != null) { - this.proxiedFor = proxyForgerData.getRecipient(); - this.proxiedBy = Crypto.toAddress(proxyForgerData.getForgerPublicKey()); + if (rewardShareData != null) { + this.recipientAccount = rewardShareData.getRecipient(); + this.mintingAccount = Crypto.toAddress(rewardShareData.getMinterPublicKey()); } else { this.address = Crypto.toAddress(this.publicKey); } @@ -52,8 +52,8 @@ public class ForgingAccountData { // Getters/Setters - public byte[] getSeed() { - return this.seed; + public byte[] getPrivateKey() { + return this.privateKey; } @XmlElement(name = "publicKey") diff --git a/src/main/java/org/qora/data/account/ProxyForgerData.java b/src/main/java/org/qora/data/account/ProxyForgerData.java deleted file mode 100644 index 455ea2fd..00000000 --- a/src/main/java/org/qora/data/account/ProxyForgerData.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.qora.data.account; - -import java.math.BigDecimal; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; - -import org.qora.crypto.Crypto; - -// All properties to be converted to JSON via JAXB -@XmlAccessorType(XmlAccessType.FIELD) -public class ProxyForgerData { - - // Properties - private byte[] forgerPublicKey; - private String recipient; - private byte[] proxyPublicKey; - private BigDecimal share; - - // Constructors - - // For JAXB - protected ProxyForgerData() { - } - - // Used when fetching from repository - public ProxyForgerData(byte[] forgerPublicKey, String recipient, byte[] proxyPublicKey, BigDecimal share) { - this.forgerPublicKey = forgerPublicKey; - this.recipient = recipient; - this.proxyPublicKey = proxyPublicKey; - this.share = share; - } - - // Getters / setters - - public byte[] getForgerPublicKey() { - return this.forgerPublicKey; - } - - public String getRecipient() { - return this.recipient; - } - - public byte[] getProxyPublicKey() { - return this.proxyPublicKey; - } - - public BigDecimal getShare() { - return this.share; - } - - @XmlElement(name = "forger") - public String getForger() { - return Crypto.toAddress(this.forgerPublicKey); - } - -} diff --git a/src/main/java/org/qora/data/account/RewardShareData.java b/src/main/java/org/qora/data/account/RewardShareData.java new file mode 100644 index 00000000..92f3de16 --- /dev/null +++ b/src/main/java/org/qora/data/account/RewardShareData.java @@ -0,0 +1,58 @@ +package org.qora.data.account; + +import java.math.BigDecimal; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; + +import org.qora.crypto.Crypto; + +// All properties to be converted to JSON via JAXB +@XmlAccessorType(XmlAccessType.FIELD) +public class RewardShareData { + + // Properties + private byte[] minterPublicKey; + private String recipient; + private byte[] rewardSharePublicKey; + private BigDecimal sharePercent; + + // Constructors + + // For JAXB + protected RewardShareData() { + } + + // Used when fetching from repository + public RewardShareData(byte[] minterPublicKey, String recipient, byte[] rewardSharePublicKey, BigDecimal sharePercent) { + this.minterPublicKey = minterPublicKey; + this.recipient = recipient; + this.rewardSharePublicKey = rewardSharePublicKey; + this.sharePercent = sharePercent; + } + + // Getters / setters + + public byte[] getMinterPublicKey() { + return this.minterPublicKey; + } + + public String getRecipient() { + return this.recipient; + } + + public byte[] getRewardSharePublicKey() { + return this.rewardSharePublicKey; + } + + public BigDecimal getSharePercent() { + return this.sharePercent; + } + + @XmlElement(name = "mintingAccount") + public String getMintingAccount() { + return Crypto.toAddress(this.minterPublicKey); + } + +} diff --git a/src/main/java/org/qora/data/block/BlockData.java b/src/main/java/org/qora/data/block/BlockData.java index 9fd10429..20c41e0d 100644 --- a/src/main/java/org/qora/data/block/BlockData.java +++ b/src/main/java/org/qora/data/block/BlockData.java @@ -27,8 +27,8 @@ public class BlockData implements Serializable { private byte[] transactionsSignature; private Integer height; private long timestamp; - private byte[] generatorPublicKey; - private byte[] generatorSignature; + private byte[] minterPublicKey; + private byte[] minterSignature; private int atCount; private BigDecimal atFees; private byte[] encodedOnlineAccounts; @@ -43,7 +43,7 @@ public class BlockData implements Serializable { } public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp, - byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees, + byte[] minterPublicKey, byte[] minterSignature, int atCount, BigDecimal atFees, byte[] encodedOnlineAccounts, int onlineAccountsCount, Long onlineAccountsTimestamp, byte[] onlineAccountsSignatures) { this.version = version; this.reference = reference; @@ -52,8 +52,8 @@ public class BlockData implements Serializable { this.transactionsSignature = transactionsSignature; this.height = height; this.timestamp = timestamp; - this.generatorPublicKey = generatorPublicKey; - this.generatorSignature = generatorSignature; + this.minterPublicKey = minterPublicKey; + this.minterSignature = minterSignature; this.atCount = atCount; this.atFees = atFees; this.encodedOnlineAccounts = encodedOnlineAccounts; @@ -61,15 +61,15 @@ public class BlockData implements Serializable { this.onlineAccountsTimestamp = onlineAccountsTimestamp; this.onlineAccountsSignatures = onlineAccountsSignatures; - if (this.generatorSignature != null && this.transactionsSignature != null) - this.signature = Bytes.concat(this.generatorSignature, this.transactionsSignature); + if (this.minterSignature != null && this.transactionsSignature != null) + this.signature = Bytes.concat(this.minterSignature, this.transactionsSignature); else this.signature = null; } public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp, - byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees) { - this(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatorPublicKey, generatorSignature, atCount, atFees, + byte[] minterPublicKey, byte[] minterSignature, int atCount, BigDecimal atFees) { + this(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, minterPublicKey, minterSignature, atCount, atFees, null, 0, null, null); } @@ -131,16 +131,16 @@ public class BlockData implements Serializable { return this.timestamp; } - public byte[] getGeneratorPublicKey() { - return this.generatorPublicKey; + public byte[] getMinterPublicKey() { + return this.minterPublicKey; } - public byte[] getGeneratorSignature() { - return this.generatorSignature; + public byte[] getMinterSignature() { + return this.minterSignature; } - public void setGeneratorSignature(byte[] generatorSignature) { - this.generatorSignature = generatorSignature; + public void setMinterSignature(byte[] minterSignature) { + this.minterSignature = minterSignature; } public int getATCount() { @@ -181,9 +181,9 @@ public class BlockData implements Serializable { // JAXB special - @XmlElement(name = "generatorAddress") - protected String getGeneratorAddress() { - return Crypto.toAddress(this.generatorPublicKey); + @XmlElement(name = "minterAddress") + protected String getMinterAddress() { + return Crypto.toAddress(this.minterPublicKey); } } diff --git a/src/main/java/org/qora/data/block/BlockSummaryData.java b/src/main/java/org/qora/data/block/BlockSummaryData.java index 7396f12d..327d2d4b 100644 --- a/src/main/java/org/qora/data/block/BlockSummaryData.java +++ b/src/main/java/org/qora/data/block/BlockSummaryData.java @@ -7,21 +7,21 @@ public class BlockSummaryData { // Properties private int height; private byte[] signature; - private byte[] generatorPublicKey; + private byte[] minterPublicKey; private int onlineAccountsCount; // Constructors - public BlockSummaryData(int height, byte[] signature, byte[] generatorPublicKey, int onlineAccountsCount) { + public BlockSummaryData(int height, byte[] signature, byte[] minterPublicKey, int onlineAccountsCount) { this.height = height; this.signature = signature; - this.generatorPublicKey = generatorPublicKey; + this.minterPublicKey = minterPublicKey; this.onlineAccountsCount = onlineAccountsCount; } public BlockSummaryData(BlockData blockData) { this.height = blockData.getHeight(); this.signature = blockData.getSignature(); - this.generatorPublicKey = blockData.getGeneratorPublicKey(); + this.minterPublicKey = blockData.getMinterPublicKey(); byte[] encodedOnlineAccounts = blockData.getEncodedOnlineAccounts(); if (encodedOnlineAccounts != null) { @@ -41,8 +41,8 @@ public class BlockSummaryData { return this.signature; } - public byte[] getGeneratorPublicKey() { - return this.generatorPublicKey; + public byte[] getMinterPublicKey() { + return this.minterPublicKey; } public int getOnlineAccountsCount() { diff --git a/src/main/java/org/qora/data/network/PeerChainTipData.java b/src/main/java/org/qora/data/network/PeerChainTipData.java index d4910aa2..d2a944a2 100644 --- a/src/main/java/org/qora/data/network/PeerChainTipData.java +++ b/src/main/java/org/qora/data/network/PeerChainTipData.java @@ -8,14 +8,14 @@ public class PeerChainTipData { private byte[] lastBlockSignature; /** Latest block timestamp as reported by peer. */ private Long lastBlockTimestamp; - /** Latest block generator public key as reported by peer. */ - private byte[] lastBlockGenerator; + /** Latest block minter public key as reported by peer. */ + private byte[] lastBlockMinter; - public PeerChainTipData(Integer lastHeight, byte[] lastBlockSignature, Long lastBlockTimestamp, byte[] lastBlockGenerator) { + public PeerChainTipData(Integer lastHeight, byte[] lastBlockSignature, Long lastBlockTimestamp, byte[] lastBlockMinter) { this.lastHeight = lastHeight; this.lastBlockSignature = lastBlockSignature; this.lastBlockTimestamp = lastBlockTimestamp; - this.lastBlockGenerator = lastBlockGenerator; + this.lastBlockMinter = lastBlockMinter; } public Integer getLastHeight() { @@ -30,8 +30,8 @@ public class PeerChainTipData { return this.lastBlockTimestamp; } - public byte[] getLastBlockGenerator() { - return this.lastBlockGenerator; + public byte[] getLastBlockMinter() { + return this.lastBlockMinter; } } diff --git a/src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java b/src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java deleted file mode 100644 index c0be4ab5..00000000 --- a/src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.qora.data.transaction; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; - -import org.qora.transaction.Transaction.TransactionType; - -import io.swagger.v3.oas.annotations.media.Schema; - -// All properties to be converted to JSON via JAXB -@XmlAccessorType(XmlAccessType.FIELD) -@Schema(allOf = {TransactionData.class}) -public class EnableForgingTransactionData extends TransactionData { - - private String target; - - // Constructors - - // For JAXB - protected EnableForgingTransactionData() { - super(TransactionType.ENABLE_FORGING); - } - - public EnableForgingTransactionData(BaseTransactionData baseTransactionData, String target) { - super(TransactionType.ENABLE_FORGING, baseTransactionData); - - this.target = target; - } - - // Getters / setters - - public String getTarget() { - return this.target; - } - - // Re-expose to JAXB - - @XmlElement(name = "creatorPublicKey") - @Schema(name = "creatorPublicKey", description = "creator's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") - public byte[] getEnableForgingCreatorPublicKey() { - return super.getCreatorPublicKey(); - } - - @XmlElement(name = "creatorPublicKey") - @Schema(name = "creatorPublicKey", description = "creator's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP") - public void setEnableForgingCreatorPublicKey(byte[] creatorPublicKey) { - super.setCreatorPublicKey(creatorPublicKey); - } - -} diff --git a/src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java b/src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java deleted file mode 100644 index 87dcce6a..00000000 --- a/src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java +++ /dev/null @@ -1,92 +0,0 @@ -package org.qora.data.transaction; - -import java.math.BigDecimal; - -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.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue; -import org.qora.transaction.Transaction.TransactionType; - -import io.swagger.v3.oas.annotations.media.Schema; - -// All properties to be converted to JSON via JAXB -@XmlAccessorType(XmlAccessType.FIELD) -@Schema(allOf = {TransactionData.class}) -// JAXB: use this subclass if XmlDiscriminatorNode matches XmlDiscriminatorValue below: -@XmlDiscriminatorValue("PROXY_FORGING") -public class ProxyForgingTransactionData extends TransactionData { - - @Schema(example = "forger_public_key") - private byte[] forgerPublicKey; - - private String recipient; - - @Schema(example = "proxy_public_key") - private byte[] proxyPublicKey; - - private BigDecimal share; - - // No need to ever expose this via API - @XmlTransient - @Schema(hidden = true) - private BigDecimal previousShare; - - // Constructors - - // For JAXB - protected ProxyForgingTransactionData() { - super(TransactionType.PROXY_FORGING); - } - - public void afterUnmarshal(Unmarshaller u, Object parent) { - this.creatorPublicKey = this.forgerPublicKey; - } - - /** From repository */ - public ProxyForgingTransactionData(BaseTransactionData baseTransactionData, - String recipient, byte[] proxyPublicKey, BigDecimal share, BigDecimal previousShare) { - super(TransactionType.PROXY_FORGING, baseTransactionData); - - this.forgerPublicKey = baseTransactionData.creatorPublicKey; - this.recipient = recipient; - this.proxyPublicKey = proxyPublicKey; - this.share = share; - this.previousShare = previousShare; - } - - /** From network/API */ - public ProxyForgingTransactionData(BaseTransactionData baseTransactionData, - String recipient, byte[] proxyPublicKey, BigDecimal share) { - this(baseTransactionData, recipient, proxyPublicKey, share, null); - } - - // Getters / setters - - public byte[] getForgerPublicKey() { - return this.forgerPublicKey; - } - - public String getRecipient() { - return this.recipient; - } - - public byte[] getProxyPublicKey() { - return this.proxyPublicKey; - } - - public BigDecimal getShare() { - return this.share; - } - - public BigDecimal getPreviousShare() { - return this.previousShare; - } - - public void setPreviousShare(BigDecimal previousShare) { - this.previousShare = previousShare; - } - -} diff --git a/src/main/java/org/qora/data/transaction/RewardShareTransactionData.java b/src/main/java/org/qora/data/transaction/RewardShareTransactionData.java new file mode 100644 index 00000000..58478611 --- /dev/null +++ b/src/main/java/org/qora/data/transaction/RewardShareTransactionData.java @@ -0,0 +1,92 @@ +package org.qora.data.transaction; + +import java.math.BigDecimal; + +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.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue; +import org.qora.transaction.Transaction.TransactionType; + +import io.swagger.v3.oas.annotations.media.Schema; + +// All properties to be converted to JSON via JAXB +@XmlAccessorType(XmlAccessType.FIELD) +@Schema(allOf = {TransactionData.class}) +// JAXB: use this subclass if XmlDiscriminatorNode matches XmlDiscriminatorValue below: +@XmlDiscriminatorValue("REWARD_SHARE") +public class RewardShareTransactionData extends TransactionData { + + @Schema(example = "minter_public_key") + private byte[] minterPublicKey; + + private String recipient; + + @Schema(example = "reward_share_public_key") + private byte[] rewardSharePublicKey; + + private BigDecimal sharePercent; + + // No need to ever expose this via API + @XmlTransient + @Schema(hidden = true) + private BigDecimal previousSharePercent; + + // Constructors + + // For JAXB + protected RewardShareTransactionData() { + super(TransactionType.REWARD_SHARE); + } + + public void afterUnmarshal(Unmarshaller u, Object parent) { + this.creatorPublicKey = this.minterPublicKey; + } + + /** From repository */ + public RewardShareTransactionData(BaseTransactionData baseTransactionData, + String recipient, byte[] rewardSharePublicKey, BigDecimal sharePercent, BigDecimal previousSharePercent) { + super(TransactionType.REWARD_SHARE, baseTransactionData); + + this.minterPublicKey = baseTransactionData.creatorPublicKey; + this.recipient = recipient; + this.rewardSharePublicKey = rewardSharePublicKey; + this.sharePercent = sharePercent; + this.previousSharePercent = previousSharePercent; + } + + /** From network/API */ + public RewardShareTransactionData(BaseTransactionData baseTransactionData, + String recipient, byte[] rewardSharePublicKey, BigDecimal sharePercent) { + this(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, null); + } + + // Getters / setters + + public byte[] getMinterPublicKey() { + return this.minterPublicKey; + } + + public String getRecipient() { + return this.recipient; + } + + public byte[] getRewardSharePublicKey() { + return this.rewardSharePublicKey; + } + + public BigDecimal getSharePercent() { + return this.sharePercent; + } + + public BigDecimal getPreviousSharePercent() { + return this.previousSharePercent; + } + + public void setPreviousSharePercent(BigDecimal previousSharePercent) { + this.previousSharePercent = previousSharePercent; + } + +} diff --git a/src/main/java/org/qora/data/transaction/TransactionData.java b/src/main/java/org/qora/data/transaction/TransactionData.java index 88915c0b..50b92fb8 100644 --- a/src/main/java/org/qora/data/transaction/TransactionData.java +++ b/src/main/java/org/qora/data/transaction/TransactionData.java @@ -39,7 +39,7 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode; JoinGroupTransactionData.class, LeaveGroupTransactionData.class, GroupApprovalTransactionData.class, SetGroupTransactionData.class, UpdateAssetTransactionData.class, - AccountFlagsTransactionData.class, EnableForgingTransactionData.class, ProxyForgingTransactionData.class, + AccountFlagsTransactionData.class, RewardShareTransactionData.class, AccountLevelTransactionData.class }) //All properties to be converted to JSON via JAXB diff --git a/src/main/java/org/qora/mintsim.java b/src/main/java/org/qora/mintsim.java deleted file mode 100644 index 168815de..00000000 --- a/src/main/java/org/qora/mintsim.java +++ /dev/null @@ -1,182 +0,0 @@ -package org.qora; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -public class mintsim { - - private static final int NUMBER_BLOCKS = 5_000_000; - private static final double GRANT_PROB = 0.001; - private static final int BLOCK_HISTORY = 0; - private static final int WEIGHTING = 2; - - private static final int TOP_MINTERS_SIZE = 200; - - private static final Random random = new Random(); - private static List tiers = new ArrayList<>(); - private static List accounts = new ArrayList<>(); - private static List blockMinters = new ArrayList<>(); - - private static List accountsWithGrants = new ArrayList<>(); - - public static class TierInfo { - public final int maxAccounts; - public final int minBlocks; - public int numberAccounts; - - public TierInfo(int maxAccounts, int minBlocks) { - this.maxAccounts = maxAccounts; - this.minBlocks = minBlocks; - this.numberAccounts = 0; - } - } - - public static class Account { - public final int tierIndex; - public int blocksForged; - public int rightsGranted; - - public Account(int tierIndex) { - this.tierIndex = tierIndex; - this.blocksForged = 0; - this.rightsGranted = 0; - } - } - - public static void main(String[] args) { - if (args.length < 2 || (args.length % 2) != 0) { - System.err.println("usage: mintsim [ [...]]"); - System.exit(1); - } - - try { - int argIndex = 0; - do { - int minBlocks = Integer.parseInt(args[argIndex++]); - int maxAccounts = Integer.parseInt(args[argIndex++]); - - tiers.add(new TierInfo(maxAccounts, minBlocks)); - } while (argIndex < args.length); - } catch (NumberFormatException e) { - System.err.println("Can't parse number?"); - System.exit(2); - } - - // Print summary - System.out.println(String.format("Number of tiers: %d", tiers.size())); - - for (int i = 0; i < tiers.size(); ++i) { - TierInfo tier = tiers.get(i); - System.out.println(String.format("Tier %d:", i)); - System.out.println(String.format("\tMinimum forged blocks to grant right: %d", tier.minBlocks)); - System.out.println(String.format("\tMaximum tier%d grants: %d", i + 1, tier.maxAccounts)); - } - - TierInfo initialTier = tiers.get(0); - - int totalAccounts = initialTier.maxAccounts; - for (int i = 1; i < tiers.size(); ++i) - totalAccounts *= 1 + tiers.get(i).maxAccounts; - - System.out.println(String.format("Total accounts: %d", totalAccounts)); - - // Create initial accounts - initialTier.numberAccounts = initialTier.maxAccounts; - for (int i = 0; i < initialTier.maxAccounts; ++i) - accounts.add(new Account(0)); - - for (int height = 1; height < NUMBER_BLOCKS; ++height) { - int minterId = pickMinterId(); - Account minter = accounts.get(minterId); - - ++minter.blocksForged; - blockMinters.add(minterId); - - if (minter.tierIndex < tiers.size() - 1) { - TierInfo nextTier = tiers.get(minter.tierIndex + 1); - - // Minter just reached threshold to grant rights - if (minter.blocksForged == nextTier.minBlocks) - accountsWithGrants.add(minterId); - } - - List accountsToRemove = new ArrayList<>(); - // Do any account with spare grants want to grant? - for (int granterId : accountsWithGrants) { - if (random.nextDouble() >= GRANT_PROB) - continue; - - Account granter = accounts.get(granterId); - TierInfo nextTier = tiers.get(granter.tierIndex + 1); - - accounts.add(new Account(granter.tierIndex + 1)); - - ++nextTier.numberAccounts; - ++granter.rightsGranted; - - if (granter.rightsGranted == nextTier.maxAccounts) - accountsToRemove.add(granterId); - } - - // Remove granters that have used their allowance - accountsWithGrants.removeAll(accountsToRemove); - - if (height % 100000 == 0) { - System.out.println(String.format("Summary after block %d:", height)); - for (int i = 0; i < tiers.size(); ++i) - System.out.println(String.format("\tTier %d: number of accounts: %d", i, tiers.get(i).numberAccounts)); - } - } - - // Top minters - List topMinters = new ArrayList<>(); - for (int i = 0; i < accounts.size(); ++i) { - topMinters.add(i); - topMinters.sort((a, b) -> Integer.compare(accounts.get(b).blocksForged, accounts.get(a).blocksForged)); - - if (topMinters.size() > TOP_MINTERS_SIZE) - topMinters.remove(TOP_MINTERS_SIZE); - } - - System.out.println(String.format("Top %d minters:", TOP_MINTERS_SIZE)); - for (int i = 0; i < topMinters.size(); ++i) { - int topMinterId = topMinters.get(i); - Account topMinter = accounts.get(topMinterId); - System.out.println(String.format("\tAccount %d (tier %d) has minted %d blocks", topMinterId, topMinter.tierIndex, topMinter.blocksForged)); - } - - for (int i = 0; i < tiers.size(); ++i) - System.out.println(String.format("Tier %d: number of accounts: %d", i, tiers.get(i).numberAccounts)); - } - - private static int pickMinterId() { - // There might not be enough block history yet... - final int blockHistory = Math.min(BLOCK_HISTORY, blockMinters.size()); - - // Weighting (W) - - // An account that HASN'T forged in the last X blocks has 1 standard chance to forge - // but an account that HAS forged Y in the last X blocks has 1 + (Y / X) * W chances to forge - // e.g. forged 25 in last 100 blocks, with weighting 8, gives (25 / 100) * 8 = 2 extra chances - - // So in X blocks there will be X * W extra chances. - // We pick winning number from (number-of-accounts + X * W) chances - int totalChances = accounts.size() + blockHistory * WEIGHTING; - int winningNumber = random.nextInt(totalChances); - - // Simple case if winning number is less than number of accounts, - // otherwise we need to handle extra chances for accounts that have forged in last X blocks. - if (winningNumber < accounts.size()) - return winningNumber; - - // Handling extra chances - - // We can work out which block in last X blocks as each block is worth W chances - int blockOffset = (winningNumber - accounts.size()) / WEIGHTING; - int blockIndex = blockMinters.size() - 1 - blockOffset; - - return blockMinters.get(blockIndex); - } - -} diff --git a/src/main/java/org/qora/network/Network.java b/src/main/java/org/qora/network/Network.java index ec7166e3..5feb5a3d 100644 --- a/src/main/java/org/qora/network/Network.java +++ b/src/main/java/org/qora/network/Network.java @@ -1006,7 +1006,7 @@ public class Network { } // HEIGHT_V2 contains way more useful info - return new HeightV2Message(blockData.getHeight(), blockData.getSignature(), blockData.getTimestamp(), blockData.getGeneratorPublicKey()); + return new HeightV2Message(blockData.getHeight(), blockData.getSignature(), blockData.getTimestamp(), blockData.getMinterPublicKey()); } public Message buildNewTransactionMessage(Peer peer, TransactionData transactionData) { diff --git a/src/main/java/org/qora/network/message/BlockSummariesMessage.java b/src/main/java/org/qora/network/message/BlockSummariesMessage.java index 5d0ef3a6..80224a4c 100644 --- a/src/main/java/org/qora/network/message/BlockSummariesMessage.java +++ b/src/main/java/org/qora/network/message/BlockSummariesMessage.java @@ -46,12 +46,12 @@ public class BlockSummariesMessage extends Message { byte[] signature = new byte[BlockTransformer.BLOCK_SIGNATURE_LENGTH]; bytes.get(signature); - byte[] generatorPublicKey = new byte[Transformer.PUBLIC_KEY_LENGTH]; - bytes.get(generatorPublicKey); + byte[] minterPublicKey = new byte[Transformer.PUBLIC_KEY_LENGTH]; + bytes.get(minterPublicKey); int onlineAccountsCount = bytes.getInt(); - BlockSummaryData blockSummary = new BlockSummaryData(height, signature, generatorPublicKey, onlineAccountsCount); + BlockSummaryData blockSummary = new BlockSummaryData(height, signature, minterPublicKey, onlineAccountsCount); blockSummaries.add(blockSummary); } @@ -68,7 +68,7 @@ public class BlockSummariesMessage extends Message { for (BlockSummaryData blockSummary : this.blockSummaries) { bytes.write(Ints.toByteArray(blockSummary.getHeight())); bytes.write(blockSummary.getSignature()); - bytes.write(blockSummary.getGeneratorPublicKey()); + bytes.write(blockSummary.getMinterPublicKey()); bytes.write(Ints.toByteArray(blockSummary.getOnlineAccountsCount())); } diff --git a/src/main/java/org/qora/network/message/HeightV2Message.java b/src/main/java/org/qora/network/message/HeightV2Message.java index 70df83bd..8a2538de 100644 --- a/src/main/java/org/qora/network/message/HeightV2Message.java +++ b/src/main/java/org/qora/network/message/HeightV2Message.java @@ -16,19 +16,19 @@ public class HeightV2Message extends Message { private int height; private byte[] signature; private long timestamp; - private byte[] generator; + private byte[] minterPublicKey; - public HeightV2Message(int height, byte[] signature, long timestamp, byte[] generator) { - this(-1, height, signature, timestamp, generator); + public HeightV2Message(int height, byte[] signature, long timestamp, byte[] minterPublicKey) { + this(-1, height, signature, timestamp, minterPublicKey); } - private HeightV2Message(int id, int height, byte[] signature, long timestamp, byte[] generator) { + private HeightV2Message(int id, int height, byte[] signature, long timestamp, byte[] minterPublicKey) { super(id, MessageType.HEIGHT_V2); this.height = height; this.signature = signature; this.timestamp = timestamp; - this.generator = generator; + this.minterPublicKey = minterPublicKey; } public int getHeight() { @@ -43,8 +43,8 @@ public class HeightV2Message extends Message { return this.timestamp; } - public byte[] getGenerator() { - return this.generator; + public byte[] getMinterPublicKey() { + return this.minterPublicKey; } public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException { @@ -55,10 +55,10 @@ public class HeightV2Message extends Message { long timestamp = bytes.getLong(); - byte[] generator = new byte[Transformer.PUBLIC_KEY_LENGTH]; - bytes.get(generator); + byte[] minterPublicKey = new byte[Transformer.PUBLIC_KEY_LENGTH]; + bytes.get(minterPublicKey); - return new HeightV2Message(id, height, signature, timestamp, generator); + return new HeightV2Message(id, height, signature, timestamp, minterPublicKey); } @Override @@ -72,7 +72,7 @@ public class HeightV2Message extends Message { bytes.write(Longs.toByteArray(this.timestamp)); - bytes.write(this.generator); + bytes.write(this.minterPublicKey); return bytes.toByteArray(); } catch (IOException e) { diff --git a/src/main/java/org/qora/repository/AccountRepository.java b/src/main/java/org/qora/repository/AccountRepository.java index fe24328d..aaf43533 100644 --- a/src/main/java/org/qora/repository/AccountRepository.java +++ b/src/main/java/org/qora/repository/AccountRepository.java @@ -4,8 +4,8 @@ import java.util.List; import org.qora.data.account.AccountBalanceData; import org.qora.data.account.AccountData; -import org.qora.data.account.ForgingAccountData; -import org.qora.data.account.ProxyForgerData; +import org.qora.data.account.MintingAccountData; +import org.qora.data.account.RewardShareData; public interface AccountRepository { @@ -29,9 +29,6 @@ public interface AccountRepository { /** Returns whether account exists. */ public boolean accountExists(String address) throws DataException; - /** Returns number of accounts enabled to forge by given address. */ - public int countForgingAccountsEnabledByAddress(String address) throws DataException; - /** * Ensures at least minimal account info in repository. *

@@ -75,11 +72,11 @@ public interface AccountRepository { public void setInitialLevel(AccountData accountData) throws DataException; /** - * Saves account's generated block count and public key if present, in repository. + * Saves account's minted block count and public key if present, in repository. *

* Note: ignores other fields like last reference, default groupID. */ - public void setBlocksGenerated(AccountData accountData) throws DataException; + public void setMintedBlockCount(AccountData accountData) throws DataException; /** Delete account from repository. */ public void delete(String address) throws DataException; @@ -100,44 +97,45 @@ public interface AccountRepository { public void delete(String address, long assetId) throws DataException; - // Proxy forging + // Reward-shares - public ProxyForgerData getProxyForgeData(byte[] forgerPublicKey, String recipient) throws DataException; + public RewardShareData getRewardShare(byte[] mintingAccountPublicKey, String recipientAccount) throws DataException; - public ProxyForgerData getProxyForgeData(byte[] proxyPublicKey) throws DataException; + public RewardShareData getRewardShare(byte[] rewardSharePublicKey) throws DataException; - public boolean isProxyPublicKey(byte[] publicKey) throws DataException; + public boolean isRewardSharePublicKey(byte[] publicKey) throws DataException; - public int countProxyAccounts(byte[] forgerPublicKey) throws DataException; + /** Returns number of active reward-shares involving passed public key as the minting account only. */ + public int countRewardShares(byte[] mintingAccountPublicKey) throws DataException; - public List getProxyAccounts() throws DataException; + public List getRewardShares() throws DataException; - public List findProxyAccounts(List recipients, List forgers, List involvedAddresses, Integer limit, Integer offset, Boolean reverse) throws DataException; + public List findRewardShares(List mintingAccounts, List recipientAccounts, List involvedAddresses, Integer limit, Integer offset, Boolean reverse) throws DataException; /** - * Returns index in list of proxy accounts (sorted by public key). + * Returns index in list of reward-shares (sorted by reward-share public key). *

* @return index (from 0) or null if publicKey not found in repository. */ - public Integer getProxyAccountIndex(byte[] publicKey) throws DataException; + public Integer getRewardShareIndex(byte[] rewardSharePublicKey) throws DataException; /** - * Returns proxy forger data using index into list of proxy accounts. + * Returns reward-share data using index into list of reward-shares (sorted by reward-share public key). */ - public ProxyForgerData getProxyAccountByIndex(int index) throws DataException; + public RewardShareData getRewardShareByIndex(int index) throws DataException; - public void save(ProxyForgerData proxyForgerData) throws DataException; + public void save(RewardShareData rewardShareData) throws DataException; - /** Delete proxy forging relationship from repository using passed forger's public key and recipient's address. */ - public void delete(byte[] forgerPublickey, String recipient) throws DataException; + /** Delete reward-share from repository using passed minting account's public key and recipient's address. */ + public void delete(byte[] mintingAccountPublickey, String recipient) throws DataException; - // Forging accounts used by BlockGenerator + // Minting accounts used by BlockMinter, potentially includes reward-shares - public List getForgingAccounts() throws DataException; + public List getMintingAccounts() throws DataException; - public void save(ForgingAccountData forgingAccountData) throws DataException; + public void save(MintingAccountData mintingAccountData) throws DataException; - /** Delete forging account info, used by BlockGenerator, from repository using passed private key. */ - public int delete(byte[] forgingAccountSeed) throws DataException; + /** Delete minting account info, used by BlockMinter, from repository using passed private key. */ + public int delete(byte[] mintingAccountPrivateKey) throws DataException; } diff --git a/src/main/java/org/qora/repository/BlockRepository.java b/src/main/java/org/qora/repository/BlockRepository.java index 084332aa..c289ba0d 100644 --- a/src/main/java/org/qora/repository/BlockRepository.java +++ b/src/main/java/org/qora/repository/BlockRepository.java @@ -2,7 +2,7 @@ package org.qora.repository; import java.util.List; -import org.qora.api.model.BlockForgerSummary; +import org.qora.api.model.BlockMinterSummary; import org.qora.data.block.BlockData; import org.qora.data.block.BlockSummaryData; import org.qora.data.block.BlockTransactionData; @@ -100,23 +100,23 @@ public interface BlockRepository { } /** - * Returns number of blocks forged by account with given public key, including proxy-forged blocks. + * Returns number of blocks minted by account/reward-share with given public key. * * @param publicKey * @return number of blocks * @throws DataException */ - public int countForgedBlocks(byte[] publicKey) throws DataException; + public int countMintedBlocks(byte[] publicKey) throws DataException; /** - * Returns summaries of block forgers, optionally limited to passed addresses. + * Returns summaries of block minters, optionally limited to passed addresses. */ - public List getBlockForgers(List addresses, Integer limit, Integer offset, Boolean reverse) throws DataException; + public List getBlockMinters(List addresses, Integer limit, Integer offset, Boolean reverse) throws DataException; /** - * Returns blocks with passed generator public key. + * Returns blocks with passed minter public key. */ - public List getBlocksWithGenerator(byte[] generatorPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException; + public List getBlocksByMinter(byte[] minterPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException; /** * Returns blocks within height range. diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java index 20dcff80..753f0e7b 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBAccountRepository.java @@ -9,8 +9,8 @@ import java.util.List; import org.qora.data.account.AccountBalanceData; import org.qora.data.account.AccountData; -import org.qora.data.account.ForgingAccountData; -import org.qora.data.account.ProxyForgerData; +import org.qora.data.account.MintingAccountData; +import org.qora.data.account.RewardShareData; import org.qora.repository.AccountRepository; import org.qora.repository.DataException; @@ -26,7 +26,7 @@ public class HSQLDBAccountRepository implements AccountRepository { @Override public AccountData getAccount(String address) throws DataException { - String sql = "SELECT reference, public_key, default_group_id, flags, initial_level, level, blocks_generated FROM Accounts WHERE account = ?"; + String sql = "SELECT reference, public_key, default_group_id, flags, initial_level, level, blocks_minted FROM Accounts WHERE account = ?"; try (ResultSet resultSet = this.repository.checkedExecute(sql, address)) { if (resultSet == null) @@ -38,9 +38,9 @@ public class HSQLDBAccountRepository implements AccountRepository { int flags = resultSet.getInt(4); int initialLevel = resultSet.getInt(5); int level = resultSet.getInt(6); - int blocksGenerated = resultSet.getInt(7); + int blocksMinted = resultSet.getInt(7); - return new AccountData(address, reference, publicKey, defaultGroupId, flags, initialLevel, level, blocksGenerated); + return new AccountData(address, reference, publicKey, defaultGroupId, flags, initialLevel, level, blocksMinted); } catch (SQLException e) { throw new DataException("Unable to fetch account info from repository", e); } @@ -112,15 +112,6 @@ public class HSQLDBAccountRepository implements AccountRepository { } } - @Override - public int countForgingAccountsEnabledByAddress(String address) throws DataException { - try (ResultSet resultSet = this.repository.checkedExecute("SELECT COUNT(*) FROM Accounts WHERE forging_enabler = ? LIMIT 1", address)) { - return resultSet.getInt(1); - } catch (SQLException e) { - throw new DataException("Unable to count forging accounts enabled in repository", e); - } - } - @Override public void ensureAccount(AccountData accountData) throws DataException { byte[] publicKey = accountData.getPublicKey(); @@ -236,10 +227,10 @@ public class HSQLDBAccountRepository implements AccountRepository { } @Override - public void setBlocksGenerated(AccountData accountData) throws DataException { + public void setMintedBlockCount(AccountData accountData) throws DataException { HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts"); - saveHelper.bind("account", accountData.getAddress()).bind("blocks_generated", accountData.getBlocksGenerated()); + saveHelper.bind("account", accountData.getAddress()).bind("blocks_minted", accountData.getBlocksMinted()); byte[] publicKey = accountData.getPublicKey(); if (publicKey != null) @@ -248,7 +239,7 @@ public class HSQLDBAccountRepository implements AccountRepository { try { saveHelper.execute(this.repository); } catch (SQLException e) { - throw new DataException("Unable to save account's generated block count into repository", e); + throw new DataException("Unable to save account's minted block count into repository", e); } } @@ -401,102 +392,102 @@ public class HSQLDBAccountRepository implements AccountRepository { } } - // Proxy forging + // Reward-Share @Override - public ProxyForgerData getProxyForgeData(byte[] forgerPublicKey, String recipient) throws DataException { - String sql = "SELECT proxy_public_key, share FROM ProxyForgers WHERE forger = ? AND recipient = ?"; + public RewardShareData getRewardShare(byte[] minterPublicKey, String recipient) throws DataException { + String sql = "SELECT reward_share_public_key, share_percent FROM RewardShares WHERE minter_public_key = ? AND recipient = ?"; - try (ResultSet resultSet = this.repository.checkedExecute(sql, forgerPublicKey, recipient)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, minterPublicKey, recipient)) { if (resultSet == null) return null; - byte[] proxyPublicKey = resultSet.getBytes(1); - BigDecimal share = resultSet.getBigDecimal(2); + byte[] rewardSharePublicKey = resultSet.getBytes(1); + BigDecimal sharePercent = resultSet.getBigDecimal(2); - return new ProxyForgerData(forgerPublicKey, recipient, proxyPublicKey, share); + return new RewardShareData(minterPublicKey, recipient, rewardSharePublicKey, sharePercent); } catch (SQLException e) { - throw new DataException("Unable to fetch proxy forge info from repository", e); + throw new DataException("Unable to fetch reward-share info from repository", e); } } @Override - public ProxyForgerData getProxyForgeData(byte[] proxyPublicKey) throws DataException { - String sql = "SELECT forger, recipient, share FROM ProxyForgers WHERE proxy_public_key = ?"; + public RewardShareData getRewardShare(byte[] rewardSharePublicKey) throws DataException { + String sql = "SELECT minter_public_key, recipient, share_percent FROM RewardShares WHERE reward_share_public_key = ?"; - try (ResultSet resultSet = this.repository.checkedExecute(sql, proxyPublicKey)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, rewardSharePublicKey)) { if (resultSet == null) return null; - byte[] forgerPublicKey = resultSet.getBytes(1); + byte[] minterPublicKey = resultSet.getBytes(1); String recipient = resultSet.getString(2); - BigDecimal share = resultSet.getBigDecimal(3); + BigDecimal sharePercent = resultSet.getBigDecimal(3); - return new ProxyForgerData(forgerPublicKey, recipient, proxyPublicKey, share); + return new RewardShareData(minterPublicKey, recipient, rewardSharePublicKey, sharePercent); } catch (SQLException e) { - throw new DataException("Unable to fetch proxy forge info from repository", e); + throw new DataException("Unable to fetch reward-share info from repository", e); } } @Override - public boolean isProxyPublicKey(byte[] publicKey) throws DataException { + public boolean isRewardSharePublicKey(byte[] publicKey) throws DataException { try { - return this.repository.exists("ProxyForgers", "proxy_public_key = ?", publicKey); + return this.repository.exists("RewardShares", "reward_share_public_key = ?", publicKey); } catch (SQLException e) { - throw new DataException("Unable to check for proxy public key in repository", e); + throw new DataException("Unable to check for reward-share public key in repository", e); } } @Override - public int countProxyAccounts(byte[] forgerPublicKey) throws DataException { - String sql = "SELECT COUNT(*) FROM ProxyForgers WHERE forger = ?"; + public int countRewardShares(byte[] minterPublicKey) throws DataException { + String sql = "SELECT COUNT(*) FROM RewardShares WHERE minter_public_key = ?"; - try (ResultSet resultSet = this.repository.checkedExecute(sql, forgerPublicKey)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql, minterPublicKey)) { return resultSet.getInt(1); } catch (SQLException e) { - throw new DataException("Unable to count proxy forging relationships in repository", e); + throw new DataException("Unable to count reward-shares in repository", e); } } @Override - public List getProxyAccounts() throws DataException { - String sql = "SELECT forger, recipient, share, proxy_public_key FROM ProxyForgers"; + public List getRewardShares() throws DataException { + String sql = "SELECT minter_public_key, recipient, share_percent, reward_share_public_key FROM RewardShares"; - List proxyAccounts = new ArrayList<>(); + List rewardShares = new ArrayList<>(); try (ResultSet resultSet = this.repository.checkedExecute(sql)) { if (resultSet == null) - return proxyAccounts; + return rewardShares; do { - byte[] forgerPublicKey = resultSet.getBytes(1); + byte[] minterPublicKey = resultSet.getBytes(1); String recipient = resultSet.getString(2); - BigDecimal share = resultSet.getBigDecimal(3); - byte[] proxyPublicKey = resultSet.getBytes(4); + BigDecimal sharePercent = resultSet.getBigDecimal(3); + byte[] rewardSharePublicKey = resultSet.getBytes(4); - proxyAccounts.add(new ProxyForgerData(forgerPublicKey, recipient, proxyPublicKey, share)); + rewardShares.add(new RewardShareData(minterPublicKey, recipient, rewardSharePublicKey, sharePercent)); } while (resultSet.next()); - return proxyAccounts; + return rewardShares; } catch (SQLException e) { - throw new DataException("Unable to fetch proxy forge accounts from repository", e); + throw new DataException("Unable to fetch reward-shares from repository", e); } } @Override - public List findProxyAccounts(List recipients, List forgers, List involvedAddresses, + public List findRewardShares(List minters, List recipients, List involvedAddresses, Integer limit, Integer offset, Boolean reverse) throws DataException { StringBuilder sql = new StringBuilder(1024); - sql.append("SELECT DISTINCT forger, recipient, share, proxy_public_key FROM ProxyForgers "); + sql.append("SELECT DISTINCT minter_public_key, recipient, share_percent, reward_share_public_key FROM RewardShares "); List args = new ArrayList<>(); final boolean hasRecipients = recipients != null && !recipients.isEmpty(); - final boolean hasForgers = forgers != null && !forgers.isEmpty(); + final boolean hasMinters = minters != null && !minters.isEmpty(); final boolean hasInvolved = involvedAddresses != null && !involvedAddresses.isEmpty(); - if (hasForgers || hasInvolved) - sql.append("JOIN Accounts ON Accounts.public_key = ProxyForgers.forger "); + if (hasMinters || hasInvolved) + sql.append("JOIN Accounts ON Accounts.public_key = RewardShares.minter_public_key "); if (hasRecipients) { sql.append("JOIN (VALUES "); @@ -509,29 +500,28 @@ public class HSQLDBAccountRepository implements AccountRepository { sql.append("(?)"); } - sql.append(") AS Recipients (address) ON ProxyForgers.recipient = Recipients.address "); + sql.append(") AS Recipients (address) ON RewardShares.recipient = Recipients.address "); args.addAll(recipients); } - if (hasForgers) { + if (hasMinters) { sql.append("JOIN (VALUES "); - final int forgersSize = forgers.size(); - for (int fi = 0; fi < forgersSize; ++fi) { + final int mintersSize = minters.size(); + for (int fi = 0; fi < mintersSize; ++fi) { if (fi != 0) sql.append(", "); sql.append("(?)"); } - sql.append(") AS Forgers (address) ON Accounts.account = Forgers.address "); - args.addAll(forgers); + sql.append(") AS Minters (address) ON Accounts.account = Minters.address "); + args.addAll(minters); } if (hasInvolved) { sql.append("JOIN (VALUES "); - final int involvedAddressesSize = involvedAddresses.size(); for (int iai = 0; iai < involvedAddressesSize; ++iai) { if (iai != 0) @@ -540,40 +530,40 @@ public class HSQLDBAccountRepository implements AccountRepository { sql.append("(?)"); } - sql.append(") AS Involved (address) ON Involved.address IN (ProxyForgers.recipient, Accounts.account) "); + sql.append(") AS Involved (address) ON Involved.address IN (RewardShares.recipient, Accounts.account) "); args.addAll(involvedAddresses); } - sql.append("ORDER BY recipient, share"); + sql.append("ORDER BY recipient, share_percent"); if (reverse != null && reverse) sql.append(" DESC"); HSQLDBRepository.limitOffsetSql(sql, limit, offset); - List proxyAccounts = new ArrayList<>(); + List rewardShares = new ArrayList<>(); try (ResultSet resultSet = this.repository.checkedExecute(sql.toString(), args.toArray())) { if (resultSet == null) - return proxyAccounts; + return rewardShares; do { - byte[] forgerPublicKey = resultSet.getBytes(1); + byte[] minterPublicKey = resultSet.getBytes(1); String recipient = resultSet.getString(2); - BigDecimal share = resultSet.getBigDecimal(3); - byte[] proxyPublicKey = resultSet.getBytes(4); + BigDecimal sharePercent = resultSet.getBigDecimal(3); + byte[] rewardSharePublicKey = resultSet.getBytes(4); - proxyAccounts.add(new ProxyForgerData(forgerPublicKey, recipient, proxyPublicKey, share)); + rewardShares.add(new RewardShareData(minterPublicKey, recipient, rewardSharePublicKey, sharePercent)); } while (resultSet.next()); - return proxyAccounts; + return rewardShares; } catch (SQLException e) { - throw new DataException("Unable to find proxy forge accounts in repository", e); + throw new DataException("Unable to find reward-shares in repository", e); } } @Override - public Integer getProxyAccountIndex(byte[] publicKey) throws DataException { - String sql = "SELECT COUNT(*) FROM ProxyForgers WHERE proxy_public_key < ?"; + public Integer getRewardShareIndex(byte[] publicKey) throws DataException { + String sql = "SELECT COUNT(*) FROM RewardShares WHERE reward_share_public_key < ?"; try (ResultSet resultSet = this.repository.checkedExecute(sql, publicKey)) { if (resultSet == null) @@ -581,92 +571,92 @@ public class HSQLDBAccountRepository implements AccountRepository { return resultSet.getInt(1); } catch (SQLException e) { - throw new DataException("Unable to determine account index in repository", e); + throw new DataException("Unable to determine reward-share index in repository", e); } } @Override - public ProxyForgerData getProxyAccountByIndex(int index) throws DataException { - String sql = "SELECT forger, recipient, share, proxy_public_key FROM ProxyForgers " - + "ORDER BY proxy_public_key ASC " + public RewardShareData getRewardShareByIndex(int index) throws DataException { + String sql = "SELECT minter_public_key, recipient, share_percent, reward_share_public_key FROM RewardShares " + + "ORDER BY reward_share_public_key ASC " + "OFFSET ? LIMIT 1"; try (ResultSet resultSet = this.repository.checkedExecute(sql, index)) { if (resultSet == null) return null; - byte[] forgerPublicKey = resultSet.getBytes(1); + byte[] minterPublicKey = resultSet.getBytes(1); String recipient = resultSet.getString(2); - BigDecimal share = resultSet.getBigDecimal(3); - byte[] proxyPublicKey = resultSet.getBytes(4); + BigDecimal sharePercent = resultSet.getBigDecimal(3); + byte[] rewardSharePublicKey = resultSet.getBytes(4); - return new ProxyForgerData(forgerPublicKey, recipient, proxyPublicKey, share); + return new RewardShareData(minterPublicKey, recipient, rewardSharePublicKey, sharePercent); } catch (SQLException e) { - throw new DataException("Unable to fetch account info from repository", e); + throw new DataException("Unable to fetch reward-share info from repository", e); } } @Override - public void save(ProxyForgerData proxyForgerData) throws DataException { - HSQLDBSaver saveHelper = new HSQLDBSaver("ProxyForgers"); + public void save(RewardShareData rewardShareData) throws DataException { + HSQLDBSaver saveHelper = new HSQLDBSaver("RewardShares"); - saveHelper.bind("forger", proxyForgerData.getForgerPublicKey()).bind("recipient", proxyForgerData.getRecipient()) - .bind("proxy_public_key", proxyForgerData.getProxyPublicKey()).bind("share", proxyForgerData.getShare()); + saveHelper.bind("minter_public_key", rewardShareData.getMinterPublicKey()).bind("recipient", rewardShareData.getRecipient()) + .bind("reward_share_public_key", rewardShareData.getRewardSharePublicKey()).bind("share_percent", rewardShareData.getSharePercent()); try { saveHelper.execute(this.repository); } catch (SQLException e) { - throw new DataException("Unable to save proxy forge info into repository", e); + throw new DataException("Unable to save reward-share info into repository", e); } } @Override - public void delete(byte[] forgerPublickey, String recipient) throws DataException { + public void delete(byte[] minterPublickey, String recipient) throws DataException { try { - this.repository.delete("ProxyForgers", "forger = ? and recipient = ?", forgerPublickey, recipient); + this.repository.delete("RewardShares", "minter_public_key = ? and recipient = ?", minterPublickey, recipient); } catch (SQLException e) { - throw new DataException("Unable to delete proxy forge info from repository", e); + throw new DataException("Unable to delete reward-share info from repository", e); } } - // Forging accounts used by BlockGenerator + // Minting accounts used by BlockMinter - public List getForgingAccounts() throws DataException { - List forgingAccounts = new ArrayList<>(); + public List getMintingAccounts() throws DataException { + List mintingAccounts = new ArrayList<>(); - try (ResultSet resultSet = this.repository.checkedExecute("SELECT forger_seed FROM ForgingAccounts")) { + try (ResultSet resultSet = this.repository.checkedExecute("SELECT minter_private_key FROM MintingAccounts")) { if (resultSet == null) - return forgingAccounts; + return mintingAccounts; do { - byte[] forgerSeed = resultSet.getBytes(1); + byte[] minterPrivateKey = resultSet.getBytes(1); - forgingAccounts.add(new ForgingAccountData(forgerSeed)); + mintingAccounts.add(new MintingAccountData(minterPrivateKey)); } while (resultSet.next()); - return forgingAccounts; + return mintingAccounts; } catch (SQLException e) { - throw new DataException("Unable to find forging accounts in repository", e); + throw new DataException("Unable to fetch minting accounts from repository", e); } } - public void save(ForgingAccountData forgingAccountData) throws DataException { - HSQLDBSaver saveHelper = new HSQLDBSaver("ForgingAccounts"); + public void save(MintingAccountData mintingAccountData) throws DataException { + HSQLDBSaver saveHelper = new HSQLDBSaver("MintingAccounts"); - saveHelper.bind("forger_seed", forgingAccountData.getSeed()); + saveHelper.bind("minter_private_key", mintingAccountData.getPrivateKey()); try { saveHelper.execute(this.repository); } catch (SQLException e) { - throw new DataException("Unable to save forging account into repository", e); + throw new DataException("Unable to save minting account into repository", e); } } - public int delete(byte[] forgingAccountSeed) throws DataException { + public int delete(byte[] minterPrivateKey) throws DataException { try { - return this.repository.delete("ForgingAccounts", "forger_seed = ?", forgingAccountSeed); + return this.repository.delete("MintingAccounts", "minter_private_key = ?", minterPrivateKey); } catch (SQLException e) { - throw new DataException("Unable to delete forging account from repository", e); + throw new DataException("Unable to delete minting account from repository", e); } } diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java index 641b2c92..8036cc0e 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBAssetRepository.java @@ -118,9 +118,9 @@ public class HSQLDBAssetRepository implements AssetRepository { long quantity = resultSet.getLong(5); boolean isDivisible = resultSet.getBoolean(6); String data = resultSet.getString(7); - boolean isUnspendable = resultSet.getBoolean(7); - int creationGroupId = resultSet.getInt(8); - byte[] reference = resultSet.getBytes(9); + boolean isUnspendable = resultSet.getBoolean(8); + int creationGroupId = resultSet.getInt(9); + byte[] reference = resultSet.getBytes(10); assets.add(new AssetData(assetId, owner, assetName, description, quantity, isDivisible, data, isUnspendable,creationGroupId, reference)); diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java index 027f7c55..f85cc94d 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import org.qora.api.model.BlockForgerSummary; +import org.qora.api.model.BlockMinterSummary; import org.qora.data.block.BlockData; import org.qora.data.block.BlockSummaryData; import org.qora.data.block.BlockTransactionData; @@ -22,7 +22,7 @@ import static org.qora.repository.hsqldb.HSQLDBRepository.getZonedTimestampMilli public class HSQLDBBlockRepository implements BlockRepository { private static final String BLOCK_DB_COLUMNS = "version, reference, transaction_count, total_fees, " - + "transactions_signature, height, generation, generator, generator_signature, " + + "transactions_signature, height, minted, minter, minter_signature, " + "AT_count, AT_fees, online_accounts, online_accounts_count, online_accounts_timestamp, online_accounts_signatures"; protected HSQLDBRepository repository; @@ -43,8 +43,8 @@ public class HSQLDBBlockRepository implements BlockRepository { byte[] transactionsSignature = resultSet.getBytes(5); int height = resultSet.getInt(6); long timestamp = getZonedTimestampMilli(resultSet, 7); - byte[] generatorPublicKey = resultSet.getBytes(8); - byte[] generatorSignature = resultSet.getBytes(9); + byte[] minterPublicKey = resultSet.getBytes(8); + byte[] minterSignature = resultSet.getBytes(9); int atCount = resultSet.getInt(10); BigDecimal atFees = resultSet.getBigDecimal(11); byte[] encodedOnlineAccounts = resultSet.getBytes(12); @@ -53,7 +53,7 @@ public class HSQLDBBlockRepository implements BlockRepository { byte[] onlineAccountsSignatures = resultSet.getBytes(15); return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, - generatorPublicKey, generatorSignature, atCount, atFees, + minterPublicKey, minterSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures); } catch (SQLException e) { throw new DataException("Error extracting data from result set", e); @@ -110,8 +110,8 @@ public class HSQLDBBlockRepository implements BlockRepository { @Override public int getHeightFromTimestamp(long timestamp) throws DataException { - // Uses (generation, height) index - try (ResultSet resultSet = this.repository.checkedExecute("SELECT height FROM Blocks WHERE generation <= ? ORDER BY generation DESC LIMIT 1", + // Uses (minted, height) index + try (ResultSet resultSet = this.repository.checkedExecute("SELECT height FROM Blocks WHERE minted <= ? ORDER BY minted DESC LIMIT 1", toOffsetDateTime(timestamp))) { if (resultSet == null) return 0; @@ -174,40 +174,40 @@ public class HSQLDBBlockRepository implements BlockRepository { } @Override - public int countForgedBlocks(byte[] publicKey) throws DataException { - String directSql = "SELECT COUNT(*) FROM Blocks WHERE generator = ?"; + public int countMintedBlocks(byte[] minterPublicKey) throws DataException { + String directSql = "SELECT COUNT(*) FROM Blocks WHERE minter = ?"; - String proxySql = "SELECT COUNT(*) FROM ProxyForgers JOIN Blocks ON generator = proxy_public_key WHERE forger = ?"; + String rewardShareSql = "SELECT COUNT(*) FROM RewardShares JOIN Blocks ON minter = reward_share_public_key WHERE minter_public_key = ?"; int totalCount = 0; - try (ResultSet resultSet = this.repository.checkedExecute(directSql, publicKey)) { + try (ResultSet resultSet = this.repository.checkedExecute(directSql, minterPublicKey)) { totalCount += resultSet.getInt(1); } catch (SQLException e) { - throw new DataException("Unable to fetch forged blocks count from repository", e); + throw new DataException("Unable to count minted blocks in repository", e); } - try (ResultSet resultSet = this.repository.checkedExecute(proxySql, publicKey)) { + try (ResultSet resultSet = this.repository.checkedExecute(rewardShareSql, minterPublicKey)) { totalCount += resultSet.getInt(1); } catch (SQLException e) { - throw new DataException("Unable to fetch forged blocks count from repository", e); + throw new DataException("Unable to count reward-share minted blocks in repository", e); } return totalCount; } @Override - public List getBlockForgers(List addresses, Integer limit, Integer offset, Boolean reverse) throws DataException { - String subquerySql = "SELECT generator, COUNT(signature) FROM Blocks GROUP BY generator"; + public List getBlockMinters(List addresses, Integer limit, Integer offset, Boolean reverse) throws DataException { + String subquerySql = "SELECT minter, COUNT(signature) FROM Blocks GROUP BY minter"; StringBuilder sql = new StringBuilder(1024); - sql.append("SELECT DISTINCT generator, n_blocks, forger, recipient FROM ("); + sql.append("SELECT DISTINCT block_minter, n_blocks, minter_public_key, recipient FROM ("); sql.append(subquerySql); - sql.append(") AS Forgers (generator, n_blocks) LEFT OUTER JOIN ProxyForgers ON proxy_public_key = generator "); + sql.append(") AS Minters (block_minter, n_blocks) LEFT OUTER JOIN RewardShares ON reward_share_public_key = block_minter "); if (addresses != null && !addresses.isEmpty()) { - sql.append(" LEFT OUTER JOIN Accounts AS GeneratorAccounts ON GeneratorAccounts.public_key = generator "); - sql.append(" LEFT OUTER JOIN Accounts AS ForgerAccounts ON ForgerAccounts.public_key = forger "); + sql.append(" LEFT OUTER JOIN Accounts AS BlockMinterAccounts ON BlockMinterAccounts.public_key = block_minter "); + sql.append(" LEFT OUTER JOIN Accounts AS RewardShareMinterAccounts ON RewardShareMinterAccounts.public_key = minter_public_key "); sql.append(" JOIN (VALUES "); final int addressesSize = addresses.size(); @@ -219,7 +219,7 @@ public class HSQLDBBlockRepository implements BlockRepository { } sql.append(") AS FilterAccounts (account) "); - sql.append(" ON FilterAccounts.account IN (recipient, GeneratorAccounts.account, ForgerAccounts.account) "); + sql.append(" ON FilterAccounts.account IN (recipient, BlockMinterAccounts.account, RewardShareMinterAccounts.account) "); } else { addresses = Collections.emptyList(); } @@ -230,31 +230,37 @@ public class HSQLDBBlockRepository implements BlockRepository { HSQLDBRepository.limitOffsetSql(sql, limit, offset); - List summaries = new ArrayList<>(); + List summaries = new ArrayList<>(); try (ResultSet resultSet = this.repository.checkedExecute(sql.toString(), addresses.toArray())) { if (resultSet == null) return summaries; do { - byte[] generator = resultSet.getBytes(1); + byte[] blockMinterPublicKey = resultSet.getBytes(1); int nBlocks = resultSet.getInt(2); - byte[] forger = resultSet.getBytes(3); - String recipient = resultSet.getString(4); + byte[] mintingAccountPublicKey = resultSet.getBytes(3); + String recipientAccount = resultSet.getString(4); - summaries.add(new BlockForgerSummary(generator, nBlocks, forger, recipient)); + BlockMinterSummary blockMinterSummary; + if (recipientAccount == null) + blockMinterSummary = new BlockMinterSummary(blockMinterPublicKey, nBlocks); + else + blockMinterSummary = new BlockMinterSummary(blockMinterPublicKey, nBlocks, mintingAccountPublicKey, recipientAccount); + + summaries.add(blockMinterSummary); } while (resultSet.next()); return summaries; } catch (SQLException e) { - throw new DataException("Unable to fetch generator's blocks from repository", e); + throw new DataException("Unable to fetch block minters from repository", e); } } @Override - public List getBlocksWithGenerator(byte[] generatorPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException { + public List getBlocksByMinter(byte[] minterPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException { StringBuilder sql = new StringBuilder(512); - sql.append("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE generator = ? ORDER BY height "); + sql.append("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE minter = ? ORDER BY height "); if (reverse != null && reverse) sql.append(" DESC"); @@ -262,7 +268,7 @@ public class HSQLDBBlockRepository implements BlockRepository { List blockData = new ArrayList<>(); - try (ResultSet resultSet = this.repository.checkedExecute(sql.toString(), generatorPublicKey)) { + try (ResultSet resultSet = this.repository.checkedExecute(sql.toString(), minterPublicKey)) { if (resultSet == null) return blockData; @@ -272,7 +278,7 @@ public class HSQLDBBlockRepository implements BlockRepository { return blockData; } catch (SQLException e) { - throw new DataException("Unable to fetch generator's blocks from repository", e); + throw new DataException("Unable to fetch minter's blocks from repository", e); } } @@ -298,7 +304,7 @@ public class HSQLDBBlockRepository implements BlockRepository { @Override public List getBlockSummaries(int firstBlockHeight, int lastBlockHeight) throws DataException { - String sql = "SELECT signature, height, generator, online_accounts_count FROM Blocks WHERE height BETWEEN ? AND ?"; + String sql = "SELECT signature, height, minter, online_accounts_count FROM Blocks WHERE height BETWEEN ? AND ?"; List blockSummaries = new ArrayList<>(); @@ -309,10 +315,10 @@ public class HSQLDBBlockRepository implements BlockRepository { do { byte[] signature = resultSet.getBytes(1); int height = resultSet.getInt(2); - byte[] generatorPublicKey = resultSet.getBytes(3); + byte[] minterPublicKey = resultSet.getBytes(3); int onlineAccountsCount = resultSet.getInt(4); - BlockSummaryData blockSummary = new BlockSummaryData(height, signature, generatorPublicKey, onlineAccountsCount); + BlockSummaryData blockSummary = new BlockSummaryData(height, signature, minterPublicKey, onlineAccountsCount); blockSummaries.add(blockSummary); } while (resultSet.next()); @@ -324,7 +330,7 @@ public class HSQLDBBlockRepository implements BlockRepository { @Override public int trimOldOnlineAccountsSignatures(long timestamp) throws DataException { - String sql = "UPDATE Blocks set online_accounts_signatures = NULL WHERE generation < ? AND online_accounts_signatures IS NOT NULL"; + String sql = "UPDATE Blocks set online_accounts_signatures = NULL WHERE minted < ? AND online_accounts_signatures IS NOT NULL"; try { return this.repository.checkedExecuteUpdateCount(sql, toOffsetDateTime(timestamp)); @@ -340,8 +346,8 @@ public class HSQLDBBlockRepository implements BlockRepository { saveHelper.bind("signature", blockData.getSignature()).bind("version", blockData.getVersion()).bind("reference", blockData.getReference()) .bind("transaction_count", blockData.getTransactionCount()).bind("total_fees", blockData.getTotalFees()) .bind("transactions_signature", blockData.getTransactionsSignature()).bind("height", blockData.getHeight()) - .bind("generation", toOffsetDateTime(blockData.getTimestamp())) - .bind("generator", blockData.getGeneratorPublicKey()).bind("generator_signature", blockData.getGeneratorSignature()) + .bind("minted", toOffsetDateTime(blockData.getTimestamp())) + .bind("minter", blockData.getMinterPublicKey()).bind("minter_signature", blockData.getMinterSignature()) .bind("AT_count", blockData.getATCount()).bind("AT_fees", blockData.getATFees()) .bind("online_accounts", blockData.getEncodedOnlineAccounts()).bind("online_accounts_count", blockData.getOnlineAccountsCount()) .bind("online_accounts_timestamp", toOffsetDateTime(blockData.getOnlineAccountsTimestamp())) diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java index 141fd32f..5b63ebc8 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBDatabaseUpdates.java @@ -810,6 +810,37 @@ public class HSQLDBDatabaseUpdates { stmt.execute("DROP TABLE EnableForgingTransactions"); break; + case 58: + // Refactoring to unify/clarify block forging/generation/proxy-forging to simply "minting" + // Account-related + stmt.execute("ALTER TABLE Accounts ALTER COLUMN blocks_generated RENAME TO blocks_minted"); + // "proxy-forging" is now "reward-share" + stmt.execute("ALTER TABLE ProxyForgers ALTER COLUMN proxy_public_key RENAME TO reward_share_public_key"); + stmt.execute("ALTER TABLE ProxyForgers ALTER COLUMN forger RENAME TO minter_public_key"); + stmt.execute("ALTER TABLE ProxyForgers ALTER COLUMN share RENAME TO share_percent"); + stmt.execute("ALTER TABLE ProxyForgers RENAME TO RewardShares"); + stmt.execute("CREATE INDEX RewardSharePublicKeyIndex ON RewardShares (reward_share_public_key)"); + stmt.execute("DROP INDEX ProxyForgersProxyPublicKeyIndex"); + // Reward-share transactions + stmt.execute("ALTER TABLE ProxyForgingTransactions ALTER COLUMN forger RENAME TO minter_public_key"); + stmt.execute("ALTER TABLE ProxyForgingTransactions ALTER COLUMN proxy_public_key RENAME TO reward_share_public_key"); + stmt.execute("ALTER TABLE ProxyForgingTransactions ALTER COLUMN share RENAME TO share_percent"); + stmt.execute("ALTER TABLE ProxyForgingTransactions ALTER COLUMN previous_share RENAME TO previous_share_percent"); + stmt.execute("ALTER TABLE ProxyForgingTransactions RENAME TO RewardShareTransactions"); + // Accounts used by BlockMinter + stmt.execute("ALTER TABLE ForgingAccounts ALTER COLUMN forger_seed RENAME TO minter_private_key"); + stmt.execute("ALTER TABLE ForgingAccounts RENAME TO MintingAccounts"); + // Blocks + stmt.execute("ALTER TABLE Blocks ALTER COLUMN generation RENAME TO minted"); + stmt.execute("ALTER TABLE Blocks ALTER COLUMN generator RENAME TO minter"); + stmt.execute("ALTER TABLE Blocks ALTER COLUMN generator_signature RENAME TO minter_signature"); + // Block-indexes + stmt.execute("CREATE INDEX BlockMinterIndex ON Blocks (minter)"); + stmt.execute("DROP INDEX BlockGeneratorIndex"); + stmt.execute("CREATE INDEX BlockMintedHeightIndex ON Blocks (minted, height)"); + stmt.execute("DROP INDEX BlockGenerationHeightIndex"); + break; + default: // nothing to do return false; diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBEnableForgingTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBEnableForgingTransactionRepository.java deleted file mode 100644 index de9b11e7..00000000 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBEnableForgingTransactionRepository.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.qora.repository.hsqldb.transaction; - -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.qora.data.transaction.EnableForgingTransactionData; -import org.qora.data.transaction.BaseTransactionData; -import org.qora.data.transaction.TransactionData; -import org.qora.repository.DataException; -import org.qora.repository.hsqldb.HSQLDBRepository; -import org.qora.repository.hsqldb.HSQLDBSaver; - -public class HSQLDBEnableForgingTransactionRepository extends HSQLDBTransactionRepository { - - public HSQLDBEnableForgingTransactionRepository(HSQLDBRepository repository) { - this.repository = repository; - } - - TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException { - String sql = "SELECT target FROM EnableForgingTransactions WHERE signature = ?"; - - try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) { - if (resultSet == null) - return null; - - String target = resultSet.getString(1); - - return new EnableForgingTransactionData(baseTransactionData, target); - } catch (SQLException e) { - throw new DataException("Unable to fetch account flags transaction from repository", e); - } - } - - @Override - public void save(TransactionData transactionData) throws DataException { - EnableForgingTransactionData enableForgingTransactionData = (EnableForgingTransactionData) transactionData; - - HSQLDBSaver saveHelper = new HSQLDBSaver("EnableForgingTransactions"); - - saveHelper.bind("signature", enableForgingTransactionData.getSignature()).bind("creator", enableForgingTransactionData.getCreatorPublicKey()) - .bind("target", enableForgingTransactionData.getTarget()); - - try { - saveHelper.execute(this.repository); - } catch (SQLException e) { - throw new DataException("Unable to save account flags transaction into repository", e); - } - } - -} diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBProxyForgingTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBProxyForgingTransactionRepository.java deleted file mode 100644 index 80e3692f..00000000 --- a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBProxyForgingTransactionRepository.java +++ /dev/null @@ -1,55 +0,0 @@ -package org.qora.repository.hsqldb.transaction; - -import java.math.BigDecimal; -import java.sql.ResultSet; -import java.sql.SQLException; - -import org.qora.data.transaction.BaseTransactionData; -import org.qora.data.transaction.ProxyForgingTransactionData; -import org.qora.data.transaction.TransactionData; -import org.qora.repository.DataException; -import org.qora.repository.hsqldb.HSQLDBRepository; -import org.qora.repository.hsqldb.HSQLDBSaver; - -public class HSQLDBProxyForgingTransactionRepository extends HSQLDBTransactionRepository { - - public HSQLDBProxyForgingTransactionRepository(HSQLDBRepository repository) { - this.repository = repository; - } - - TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException { - String sql = "SELECT recipient, proxy_public_key, share, previous_share FROM ProxyForgingTransactions WHERE signature = ?"; - - try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) { - if (resultSet == null) - return null; - - String recipient = resultSet.getString(1); - byte[] proxyPublicKey = resultSet.getBytes(2); - BigDecimal share = resultSet.getBigDecimal(3); - BigDecimal previousShare = resultSet.getBigDecimal(4); - - return new ProxyForgingTransactionData(baseTransactionData, recipient, proxyPublicKey, share, previousShare); - } catch (SQLException e) { - throw new DataException("Unable to fetch proxy forging transaction from repository", e); - } - } - - @Override - public void save(TransactionData transactionData) throws DataException { - ProxyForgingTransactionData proxyForgingTransactionData = (ProxyForgingTransactionData) transactionData; - - HSQLDBSaver saveHelper = new HSQLDBSaver("ProxyForgingTransactions"); - - saveHelper.bind("signature", proxyForgingTransactionData.getSignature()).bind("forger", proxyForgingTransactionData.getForgerPublicKey()) - .bind("recipient", proxyForgingTransactionData.getRecipient()).bind("proxy_public_key", proxyForgingTransactionData.getProxyPublicKey()) - .bind("share", proxyForgingTransactionData.getShare()).bind("previous_share", proxyForgingTransactionData.getPreviousShare()); - - try { - saveHelper.execute(this.repository); - } catch (SQLException e) { - throw new DataException("Unable to save proxy forging transaction into repository", e); - } - } - -} diff --git a/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java new file mode 100644 index 00000000..4a1ea40f --- /dev/null +++ b/src/main/java/org/qora/repository/hsqldb/transaction/HSQLDBRewardShareTransactionRepository.java @@ -0,0 +1,55 @@ +package org.qora.repository.hsqldb.transaction; + +import java.math.BigDecimal; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.qora.data.transaction.BaseTransactionData; +import org.qora.data.transaction.RewardShareTransactionData; +import org.qora.data.transaction.TransactionData; +import org.qora.repository.DataException; +import org.qora.repository.hsqldb.HSQLDBRepository; +import org.qora.repository.hsqldb.HSQLDBSaver; + +public class HSQLDBRewardShareTransactionRepository extends HSQLDBTransactionRepository { + + public HSQLDBRewardShareTransactionRepository(HSQLDBRepository repository) { + this.repository = repository; + } + + TransactionData fromBase(BaseTransactionData baseTransactionData) throws DataException { + String sql = "SELECT recipient, reward_share_public_key, share_percent, previous_share_percent FROM RewardShareTransactions WHERE signature = ?"; + + try (ResultSet resultSet = this.repository.checkedExecute(sql, baseTransactionData.getSignature())) { + if (resultSet == null) + return null; + + String recipient = resultSet.getString(1); + byte[] rewardSharePublicKey = resultSet.getBytes(2); + BigDecimal sharePercent = resultSet.getBigDecimal(3); + BigDecimal previousSharePercent = resultSet.getBigDecimal(4); + + return new RewardShareTransactionData(baseTransactionData, recipient, rewardSharePublicKey, sharePercent, previousSharePercent); + } catch (SQLException e) { + throw new DataException("Unable to fetch reward-share transaction from repository", e); + } + } + + @Override + public void save(TransactionData transactionData) throws DataException { + RewardShareTransactionData rewardShareTransactionData = (RewardShareTransactionData) transactionData; + + HSQLDBSaver saveHelper = new HSQLDBSaver("RewardShareTransactions"); + + saveHelper.bind("signature", rewardShareTransactionData.getSignature()).bind("minter_public_key", rewardShareTransactionData.getMinterPublicKey()) + .bind("recipient", rewardShareTransactionData.getRecipient()).bind("reward_share_public_key", rewardShareTransactionData.getRewardSharePublicKey()) + .bind("share_percent", rewardShareTransactionData.getSharePercent()).bind("previous_share_percent", rewardShareTransactionData.getPreviousSharePercent()); + + try { + saveHelper.execute(this.repository); + } catch (SQLException e) { + throw new DataException("Unable to save reward-share transaction into repository", e); + } + } + +} diff --git a/src/main/java/org/qora/settings/Settings.java b/src/main/java/org/qora/settings/Settings.java index f9fb0858..f692b966 100644 --- a/src/main/java/org/qora/settings/Settings.java +++ b/src/main/java/org/qora/settings/Settings.java @@ -75,7 +75,7 @@ public class Settings { private boolean isTestNet = false; /** Port number for inbound peer-to-peer connections. */ private Integer listenPort; - /** Minimum number of peers to allow block generation / synchronization. */ + /** Minimum number of peers to allow block minting / synchronization. */ private int minBlockchainPeers = 5; /** Target number of outbound connections to peers we should make. */ private int minOutboundPeers = 20; diff --git a/src/main/java/org/qora/transaction/AccountFlagsTransaction.java b/src/main/java/org/qora/transaction/AccountFlagsTransaction.java index ad537ba3..9a3f010a 100644 --- a/src/main/java/org/qora/transaction/AccountFlagsTransaction.java +++ b/src/main/java/org/qora/transaction/AccountFlagsTransaction.java @@ -104,6 +104,12 @@ public class AccountFlagsTransaction extends Transaction { target.setFlags(newFlags); } + @Override + public void processReferencesAndFees() throws DataException { + // Set account's reference + getTarget().setLastReference(this.accountFlagsTransactionData.getSignature()); + } + @Override public void orphan() throws DataException { // Revert diff --git a/src/main/java/org/qora/transaction/AccountLevelTransaction.java b/src/main/java/org/qora/transaction/AccountLevelTransaction.java index 018a339f..8427ec68 100644 --- a/src/main/java/org/qora/transaction/AccountLevelTransaction.java +++ b/src/main/java/org/qora/transaction/AccountLevelTransaction.java @@ -94,6 +94,12 @@ public class AccountLevelTransaction extends Transaction { target.setInitialLevel(this.accountLevelTransactionData.getLevel()); } + @Override + public void processReferencesAndFees() throws DataException { + // Set account's reference + getTarget().setLastReference(this.accountLevelTransactionData.getSignature()); + } + @Override public void orphan() throws DataException { // Revert diff --git a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java b/src/main/java/org/qora/transaction/ProxyForgingTransaction.java deleted file mode 100644 index 1600eb4f..00000000 --- a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java +++ /dev/null @@ -1,193 +0,0 @@ -package org.qora.transaction; - -import java.math.BigDecimal; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.qora.account.Account; -import org.qora.account.PublicKeyAccount; -import org.qora.asset.Asset; -import org.qora.block.BlockChain; -import org.qora.crypto.Crypto; -import org.qora.data.account.ProxyForgerData; -import org.qora.data.transaction.ProxyForgingTransactionData; -import org.qora.data.transaction.TransactionData; -import org.qora.repository.DataException; -import org.qora.repository.Repository; -import org.qora.transform.Transformer; - -public class ProxyForgingTransaction extends Transaction { - - // Properties - private ProxyForgingTransactionData proxyForgingTransactionData; - - // Constructors - - public ProxyForgingTransaction(Repository repository, TransactionData transactionData) { - super(repository, transactionData); - - this.proxyForgingTransactionData = (ProxyForgingTransactionData) this.transactionData; - } - - // More information - - @Override - public List getRecipientAccounts() throws DataException { - return Collections.emptyList(); - } - - @Override - public boolean isInvolved(Account account) throws DataException { - String address = account.getAddress(); - - if (address.equals(this.getForger().getAddress())) - return true; - - if (address.equals(this.getRecipient().getAddress())) - return true; - - return false; - } - - @Override - public BigDecimal getAmount(Account account) throws DataException { - String address = account.getAddress(); - BigDecimal amount = BigDecimal.ZERO.setScale(8); - - if (address.equals(this.getForger().getAddress())) - amount = amount.subtract(this.transactionData.getFee()); - - return amount; - } - - // Navigation - - public PublicKeyAccount getForger() { - return new PublicKeyAccount(this.repository, this.proxyForgingTransactionData.getForgerPublicKey()); - } - - public Account getRecipient() { - return new Account(this.repository, this.proxyForgingTransactionData.getRecipient()); - } - - // Processing - - private static final BigDecimal MAX_SHARE = BigDecimal.valueOf(100).setScale(2); - - @Override - public ValidationResult isValid() throws DataException { - // Check reward share given to recipient - if (this.proxyForgingTransactionData.getShare().compareTo(BigDecimal.ZERO) < 0 - || this.proxyForgingTransactionData.getShare().compareTo(MAX_SHARE) > 0) - return ValidationResult.INVALID_FORGE_SHARE; - - PublicKeyAccount creator = getCreator(); - - // Creator themselves needs to be allowed to forge - if (!creator.canForge()) - return ValidationResult.NO_FORGING_PERMISSION; - - // Check proxy public key is correct length - if (this.proxyForgingTransactionData.getProxyPublicKey().length != Transformer.PUBLIC_KEY_LENGTH) - return ValidationResult.INVALID_PUBLIC_KEY; - - Account recipient = getRecipient(); - if (!Crypto.isValidAddress(recipient.getAddress())) - return ValidationResult.INVALID_ADDRESS; - - // If proxy public key already exists in repository, then it must be for the same forger-recipient combo - ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.proxyForgingTransactionData.getProxyPublicKey()); - if (proxyForgerData != null) { - if (!proxyForgerData.getRecipient().equals(recipient.getAddress()) || !Arrays.equals(proxyForgerData.getForgerPublicKey(), creator.getPublicKey())) - return ValidationResult.INVALID_PUBLIC_KEY; - } else { - // This is a new relationship - - // No point starting a new relationship with 0% share (i.e. delete relationship) - if (this.proxyForgingTransactionData.getShare().compareTo(BigDecimal.ZERO) == 0) - return ValidationResult.INVALID_FORGE_SHARE; - - // Check that the generator hasn't reach maximum number of relationships - int relationshipCount = this.repository.getAccountRepository().countProxyAccounts(creator.getPublicKey()); - if (relationshipCount >= BlockChain.getInstance().getMaxProxyRelationships()) - return ValidationResult.MAXIMUM_PROXY_RELATIONSHIPS; - } - - // Check fee is positive - if (proxyForgingTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) - return ValidationResult.NEGATIVE_FEE; - - // Check creator has enough funds - if (creator.getConfirmedBalance(Asset.QORT).compareTo(proxyForgingTransactionData.getFee()) < 0) - return ValidationResult.NO_BALANCE; - - return ValidationResult.OK; - } - - @Override - public void process() throws DataException { - PublicKeyAccount forger = getForger(); - - // Grab any previous share info for orphaning purposes - ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(forger.getPublicKey(), - proxyForgingTransactionData.getRecipient()); - - if (proxyForgerData != null) - proxyForgingTransactionData.setPreviousShare(proxyForgerData.getShare()); - - // Save this transaction, with previous share info - this.repository.getTransactionRepository().save(proxyForgingTransactionData); - - // 0% share is actually a request to delete existing relationship - if (proxyForgingTransactionData.getShare().compareTo(BigDecimal.ZERO) == 0) { - this.repository.getAccountRepository().delete(forger.getPublicKey(), proxyForgingTransactionData.getRecipient()); - } else { - // Save proxy forging info - proxyForgerData = new ProxyForgerData(forger.getPublicKey(), proxyForgingTransactionData.getRecipient(), proxyForgingTransactionData.getProxyPublicKey(), proxyForgingTransactionData.getShare()); - this.repository.getAccountRepository().save(proxyForgerData); - } - } - - @Override - public void processReferencesAndFees() throws DataException { - super.processReferencesAndFees(); - - // If proxy recipient has no last-reference then use this transaction's signature as last-reference so they can spend their block rewards - Account recipient = new Account(this.repository, proxyForgingTransactionData.getRecipient()); - if (recipient.getLastReference() == null) - recipient.setLastReference(proxyForgingTransactionData.getSignature()); - } - - @Override - public void orphan() throws DataException { - // Revert - PublicKeyAccount forger = getForger(); - - if (proxyForgingTransactionData.getPreviousShare() != null) { - // Revert previous sharing arrangement - ProxyForgerData proxyForgerData = new ProxyForgerData(forger.getPublicKey(), proxyForgingTransactionData.getRecipient(), - proxyForgingTransactionData.getProxyPublicKey(), proxyForgingTransactionData.getPreviousShare()); - - this.repository.getAccountRepository().save(proxyForgerData); - } else { - // No previous arrangement so simply delete - this.repository.getAccountRepository().delete(forger.getPublicKey(), proxyForgingTransactionData.getRecipient()); - } - - // Save this transaction, with removed previous share info - proxyForgingTransactionData.setPreviousShare(null); - this.repository.getTransactionRepository().save(proxyForgingTransactionData); - } - - @Override - public void orphanReferencesAndFees() throws DataException { - super.orphanReferencesAndFees(); - - // If recipient didn't have a last-reference prior to this transaction then remove it - Account recipient = new Account(this.repository, proxyForgingTransactionData.getRecipient()); - if (Arrays.equals(recipient.getLastReference(), proxyForgingTransactionData.getSignature())) - recipient.setLastReference(null); - } - -} diff --git a/src/main/java/org/qora/transaction/RewardShareTransaction.java b/src/main/java/org/qora/transaction/RewardShareTransaction.java new file mode 100644 index 00000000..4549c367 --- /dev/null +++ b/src/main/java/org/qora/transaction/RewardShareTransaction.java @@ -0,0 +1,221 @@ +package org.qora.transaction; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.qora.account.Account; +import org.qora.account.PublicKeyAccount; +import org.qora.asset.Asset; +import org.qora.block.BlockChain; +import org.qora.crypto.Crypto; +import org.qora.data.account.RewardShareData; +import org.qora.data.transaction.RewardShareTransactionData; +import org.qora.data.transaction.TransactionData; +import org.qora.repository.DataException; +import org.qora.repository.Repository; +import org.qora.transform.Transformer; + +public class RewardShareTransaction extends Transaction { + + // Properties + private RewardShareTransactionData rewardShareTransactionData; + + // Constructors + + public RewardShareTransaction(Repository repository, TransactionData transactionData) { + super(repository, transactionData); + + this.rewardShareTransactionData = (RewardShareTransactionData) this.transactionData; + } + + // More information + + @Override + public List getRecipientAccounts() throws DataException { + return Collections.emptyList(); + } + + @Override + public boolean isInvolved(Account account) throws DataException { + String address = account.getAddress(); + + if (address.equals(this.getMintingAccount().getAddress())) + return true; + + if (address.equals(this.getRecipient().getAddress())) + return true; + + return false; + } + + @Override + public BigDecimal getAmount(Account account) throws DataException { + String address = account.getAddress(); + BigDecimal amount = BigDecimal.ZERO.setScale(8); + + if (address.equals(this.getMintingAccount().getAddress())) + amount = amount.subtract(this.transactionData.getFee()); + + return amount; + } + + // Navigation + + public PublicKeyAccount getMintingAccount() { + return new PublicKeyAccount(this.repository, this.rewardShareTransactionData.getMinterPublicKey()); + } + + public Account getRecipient() { + return new Account(this.repository, this.rewardShareTransactionData.getRecipient()); + } + + // Processing + + private static final BigDecimal MAX_SHARE = BigDecimal.valueOf(100).setScale(2); + + @Override + public ValidationResult isValid() throws DataException { + // Check reward share given to recipient + if (this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) < 0 + || this.rewardShareTransactionData.getSharePercent().compareTo(MAX_SHARE) > 0) + return ValidationResult.INVALID_REWARD_SHARE_PERCENT; + + PublicKeyAccount creator = getCreator(); + + // Check reward-share public key is correct length + if (this.rewardShareTransactionData.getRewardSharePublicKey().length != Transformer.PUBLIC_KEY_LENGTH) + return ValidationResult.INVALID_PUBLIC_KEY; + + Account recipient = getRecipient(); + if (!Crypto.isValidAddress(recipient.getAddress())) + return ValidationResult.INVALID_ADDRESS; + + // Creator themselves needs to be allowed to mint + if (!creator.canMint()) + return ValidationResult.NOT_MINTING_ACCOUNT; + + // Qortal: special rules in play depending whether recipient is also minter + final boolean isRecipientAlsoMinter = creator.getAddress().equals(recipient.getAddress()); + if (!isRecipientAlsoMinter && !creator.canRewardShare()) + return ValidationResult.ACCOUNT_CANNOT_REWARD_SHARE; + + // Look up any existing reward-share (using transaction's reward-share public key) + RewardShareData rewardShareData = this.repository.getAccountRepository().getRewardShare(this.rewardShareTransactionData.getRewardSharePublicKey()); + if (rewardShareData != null) { + // If reward-share public key already exists in repository, then it must be for the same minter-recipient combo + if (!rewardShareData.getRecipient().equals(recipient.getAddress()) || !Arrays.equals(rewardShareData.getMinterPublicKey(), creator.getPublicKey())) + return ValidationResult.INVALID_PUBLIC_KEY; + + } else { + // No luck, try looking up existing reward-share using minting & recipient account info + rewardShareData = this.repository.getAccountRepository().getRewardShare(creator.getPublicKey(), recipient.getAddress()); + + if (rewardShareData != null) + // If reward-share between minter & recipient already exists in repository, then it must be have the same public key + if (!Arrays.equals(rewardShareData.getRewardSharePublicKey(), this.rewardShareTransactionData.getRewardSharePublicKey())) + return ValidationResult.INVALID_PUBLIC_KEY; + } + + final boolean isSharePercentZero = this.rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) == 0; + + if (rewardShareData == null) { + // This is a new reward-share + + // No point starting a new reward-share with 0% share (i.e. delete relationship) + if (isSharePercentZero) + return ValidationResult.INVALID_REWARD_SHARE_PERCENT; + + // Check the minting account hasn't reach maximum number of reward-shares + int relationshipCount = this.repository.getAccountRepository().countRewardShares(creator.getPublicKey()); + if (relationshipCount >= BlockChain.getInstance().getMaxRewardSharesPerMintingAccount()) + return ValidationResult.MAXIMUM_REWARD_SHARES; + } else { + // This transaction intends to modify/terminate an existing reward-share + + // Modifying an existing self-share is pointless and forbidden (due to 0 fee). Deleting self-share is OK though. + if (isRecipientAlsoMinter && !isSharePercentZero) + return ValidationResult.INVALID_REWARD_SHARE_PERCENT; + } + + // Fee checking needed if not setting up new self-share + if (!(isRecipientAlsoMinter && rewardShareData == null)) { + // Check fee is positive + if (rewardShareTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) + return ValidationResult.NEGATIVE_FEE; + + // Check creator has enough funds + if (creator.getConfirmedBalance(Asset.QORT).compareTo(rewardShareTransactionData.getFee()) < 0) + return ValidationResult.NO_BALANCE; + } + + return ValidationResult.OK; + } + + @Override + public void process() throws DataException { + PublicKeyAccount mintingAccount = getMintingAccount(); + + // Grab any previous share info for orphaning purposes + RewardShareData rewardShareData = this.repository.getAccountRepository().getRewardShare(mintingAccount.getPublicKey(), + rewardShareTransactionData.getRecipient()); + + if (rewardShareData != null) + rewardShareTransactionData.setPreviousSharePercent(rewardShareData.getSharePercent()); + + // Save this transaction, with previous share info + this.repository.getTransactionRepository().save(rewardShareTransactionData); + + // 0% share is actually a request to delete existing reward-share + if (rewardShareTransactionData.getSharePercent().compareTo(BigDecimal.ZERO) == 0) { + this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient()); + } else { + // Save reward-share info + rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient(), rewardShareTransactionData.getRewardSharePublicKey(), rewardShareTransactionData.getSharePercent()); + this.repository.getAccountRepository().save(rewardShareData); + } + } + + @Override + public void processReferencesAndFees() throws DataException { + super.processReferencesAndFees(); + + // If reward-share recipient has no last-reference then use this transaction's signature as last-reference so they can spend their block rewards + Account recipient = new Account(this.repository, rewardShareTransactionData.getRecipient()); + if (recipient.getLastReference() == null) + recipient.setLastReference(rewardShareTransactionData.getSignature()); + } + + @Override + public void orphan() throws DataException { + // Revert + PublicKeyAccount mintingAccount = getMintingAccount(); + + if (rewardShareTransactionData.getPreviousSharePercent() != null) { + // Revert previous sharing arrangement + RewardShareData rewardShareData = new RewardShareData(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient(), + rewardShareTransactionData.getRewardSharePublicKey(), rewardShareTransactionData.getPreviousSharePercent()); + + this.repository.getAccountRepository().save(rewardShareData); + } else { + // No previous arrangement so simply delete + this.repository.getAccountRepository().delete(mintingAccount.getPublicKey(), rewardShareTransactionData.getRecipient()); + } + + // Save this transaction, with removed previous share info + rewardShareTransactionData.setPreviousSharePercent(null); + this.repository.getTransactionRepository().save(rewardShareTransactionData); + } + + @Override + public void orphanReferencesAndFees() throws DataException { + super.orphanReferencesAndFees(); + + // If recipient didn't have a last-reference prior to this transaction then remove it + Account recipient = new Account(this.repository, rewardShareTransactionData.getRecipient()); + if (Arrays.equals(recipient.getLastReference(), rewardShareTransactionData.getSignature())) + recipient.setLastReference(null); + } + +} diff --git a/src/main/java/org/qora/transaction/Transaction.java b/src/main/java/org/qora/transaction/Transaction.java index 98f009b8..5eb027f5 100644 --- a/src/main/java/org/qora/transaction/Transaction.java +++ b/src/main/java/org/qora/transaction/Transaction.java @@ -82,7 +82,7 @@ public abstract class Transaction { UPDATE_ASSET(35, true), ACCOUNT_FLAGS(36, false), ENABLE_FORGING(37, false), - PROXY_FORGING(38, false), + REWARD_SHARE(38, false), ACCOUNT_LEVEL(39, false); public final int value; @@ -224,23 +224,21 @@ public abstract class Transaction { INVALID_ASSET_OWNER(70), AT_IS_FINISHED(71), NO_FLAG_PERMISSION(72), - NO_FORGING_PERMISSION(73), - FORGING_ALREADY_ENABLED(74), - FORGE_MORE_BLOCKS(75), - FORGING_ENABLE_LIMIT(76), - INVALID_FORGE_SHARE(77), + NOT_MINTING_ACCOUNT(73), + INVALID_REWARD_SHARE_PERCENT(77), PUBLIC_KEY_UNKNOWN(78), INVALID_PUBLIC_KEY(79), AT_UNKNOWN(80), AT_ALREADY_EXISTS(81), GROUP_APPROVAL_NOT_REQUIRED(82), GROUP_APPROVAL_DECIDED(83), - MAXIMUM_PROXY_RELATIONSHIPS(84), + MAXIMUM_REWARD_SHARES(84), TRANSACTION_ALREADY_EXISTS(85), NO_BLOCKCHAIN_LOCK(86), ORDER_ALREADY_CLOSED(87), CLOCK_NOT_SYNCED(88), ASSET_NOT_SPENDABLE(89), + ACCOUNT_CANNOT_REWARD_SHARE(90), NOT_YET_RELEASED(1000); public final int value; diff --git a/src/main/java/org/qora/transform/block/BlockTransformer.java b/src/main/java/org/qora/transform/block/BlockTransformer.java index 32ab8d13..e863c9c2 100644 --- a/src/main/java/org/qora/transform/block/BlockTransformer.java +++ b/src/main/java/org/qora/transform/block/BlockTransformer.java @@ -34,16 +34,16 @@ public class BlockTransformer extends Transformer { private static final int VERSION_LENGTH = INT_LENGTH; private static final int TRANSACTIONS_SIGNATURE_LENGTH = SIGNATURE_LENGTH; - private static final int GENERATOR_SIGNATURE_LENGTH = SIGNATURE_LENGTH; - private static final int BLOCK_REFERENCE_LENGTH = GENERATOR_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_LENGTH; + private static final int MINTER_SIGNATURE_LENGTH = SIGNATURE_LENGTH; + private static final int BLOCK_REFERENCE_LENGTH = MINTER_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_LENGTH; private static final int TIMESTAMP_LENGTH = LONG_LENGTH; - private static final int GENERATOR_LENGTH = PUBLIC_KEY_LENGTH; + private static final int MINTER_PUBLIC_KEY_LENGTH = PUBLIC_KEY_LENGTH; private static final int TRANSACTION_COUNT_LENGTH = INT_LENGTH; - private static final int BASE_LENGTH = VERSION_LENGTH + TIMESTAMP_LENGTH + BLOCK_REFERENCE_LENGTH + GENERATOR_LENGTH - + TRANSACTIONS_SIGNATURE_LENGTH + GENERATOR_SIGNATURE_LENGTH + TRANSACTION_COUNT_LENGTH; + private static final int BASE_LENGTH = VERSION_LENGTH + TIMESTAMP_LENGTH + BLOCK_REFERENCE_LENGTH + MINTER_PUBLIC_KEY_LENGTH + + TRANSACTIONS_SIGNATURE_LENGTH + MINTER_SIGNATURE_LENGTH + TRANSACTION_COUNT_LENGTH; - public static final int BLOCK_SIGNATURE_LENGTH = GENERATOR_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_LENGTH; + public static final int BLOCK_SIGNATURE_LENGTH = MINTER_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_LENGTH; protected static final int TRANSACTION_SIZE_LENGTH = INT_LENGTH; // per transaction protected static final int AT_BYTES_LENGTH = INT_LENGTH; protected static final int AT_FEES_LENGTH = LONG_LENGTH; @@ -97,13 +97,13 @@ public class BlockTransformer extends Transformer { byte[] reference = new byte[BLOCK_REFERENCE_LENGTH]; byteBuffer.get(reference); - byte[] generatorPublicKey = Serialization.deserializePublicKey(byteBuffer); + byte[] minterPublicKey = Serialization.deserializePublicKey(byteBuffer); byte[] transactionsSignature = new byte[TRANSACTIONS_SIGNATURE_LENGTH]; byteBuffer.get(transactionsSignature); - byte[] generatorSignature = new byte[GENERATOR_SIGNATURE_LENGTH]; - byteBuffer.get(generatorSignature); + byte[] minterSignature = new byte[MINTER_SIGNATURE_LENGTH]; + byteBuffer.get(minterSignature); BigDecimal totalFees = BigDecimal.ZERO.setScale(8); @@ -242,7 +242,7 @@ public class BlockTransformer extends Transformer { // We don't have a height! Integer height = null; BlockData blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, - generatorPublicKey, generatorSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures); + minterPublicKey, minterSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures); return new Triple<>(blockData, transactions, atStates); } @@ -286,9 +286,9 @@ public class BlockTransformer extends Transformer { bytes.write(Ints.toByteArray(blockData.getVersion())); bytes.write(Longs.toByteArray(blockData.getTimestamp())); bytes.write(blockData.getReference()); - bytes.write(blockData.getGeneratorPublicKey()); + bytes.write(blockData.getMinterPublicKey()); bytes.write(blockData.getTransactionsSignature()); - bytes.write(blockData.getGeneratorSignature()); + bytes.write(blockData.getMinterSignature()); if (blockData.getVersion() >= 4) { int atBytesLength = blockData.getATCount() * V4_AT_ENTRY_LENGTH; @@ -360,26 +360,26 @@ public class BlockTransformer extends Transformer { } } - public static byte[] getGeneratorSignatureFromReference(byte[] blockReference) { - return Arrays.copyOf(blockReference, GENERATOR_SIGNATURE_LENGTH); + public static byte[] getMinterSignatureFromReference(byte[] blockReference) { + return Arrays.copyOf(blockReference, MINTER_SIGNATURE_LENGTH); } - public static byte[] getBytesForGeneratorSignature(BlockData blockData) throws TransformationException { - byte[] generatorSignature = getGeneratorSignatureFromReference(blockData.getReference()); - PublicKeyAccount generator = new PublicKeyAccount(null, blockData.getGeneratorPublicKey()); + public static byte[] getBytesForMinterSignature(BlockData blockData) throws TransformationException { + byte[] minterSignature = getMinterSignatureFromReference(blockData.getReference()); + PublicKeyAccount minter = new PublicKeyAccount(null, blockData.getMinterPublicKey()); - return getBytesForGeneratorSignature(generatorSignature, generator, blockData.getEncodedOnlineAccounts()); + return getBytesForMinterSignature(minterSignature, minter, blockData.getEncodedOnlineAccounts()); } - public static byte[] getBytesForGeneratorSignature(byte[] generatorSignature, PublicKeyAccount generator, byte[] encodedOnlineAccounts) + public static byte[] getBytesForMinterSignature(byte[] minterSignature, PublicKeyAccount minter, byte[] encodedOnlineAccounts) throws TransformationException { try { - ByteArrayOutputStream bytes = new ByteArrayOutputStream(GENERATOR_SIGNATURE_LENGTH + GENERATOR_LENGTH + encodedOnlineAccounts.length); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(MINTER_SIGNATURE_LENGTH + MINTER_PUBLIC_KEY_LENGTH + encodedOnlineAccounts.length); - bytes.write(generatorSignature); + bytes.write(minterSignature); - // We're padding here just in case the generator is the genesis account whose public key is only 8 bytes long. - bytes.write(Bytes.ensureCapacity(generator.getPublicKey(), GENERATOR_LENGTH, 0)); + // We're padding here just in case the minter is the genesis account whose public key is only 8 bytes long. + bytes.write(Bytes.ensureCapacity(minter.getPublicKey(), MINTER_PUBLIC_KEY_LENGTH, 0)); bytes.write(encodedOnlineAccounts); @@ -393,9 +393,9 @@ public class BlockTransformer extends Transformer { try { List transactions = block.getTransactions(); - ByteArrayOutputStream bytes = new ByteArrayOutputStream(GENERATOR_SIGNATURE_LENGTH + transactions.size() * TransactionTransformer.SIGNATURE_LENGTH); + ByteArrayOutputStream bytes = new ByteArrayOutputStream(MINTER_SIGNATURE_LENGTH + transactions.size() * TransactionTransformer.SIGNATURE_LENGTH); - bytes.write(block.getBlockData().getGeneratorSignature()); + bytes.write(block.getBlockData().getMinterSignature()); for (Transaction transaction : transactions) { // We don't include AT-Transactions as AT-state/output is dealt with elsewhere in the block code diff --git a/src/main/java/org/qora/transform/transaction/ProxyForgingTransactionTransformer.java b/src/main/java/org/qora/transform/transaction/RewardShareTransactionTransformer.java similarity index 62% rename from src/main/java/org/qora/transform/transaction/ProxyForgingTransactionTransformer.java rename to src/main/java/org/qora/transform/transaction/RewardShareTransactionTransformer.java index 1b884412..40db4eb2 100644 --- a/src/main/java/org/qora/transform/transaction/ProxyForgingTransactionTransformer.java +++ b/src/main/java/org/qora/transform/transaction/RewardShareTransactionTransformer.java @@ -7,13 +7,13 @@ import java.nio.ByteBuffer; import org.qora.block.BlockChain; import org.qora.data.transaction.BaseTransactionData; -import org.qora.data.transaction.ProxyForgingTransactionData; +import org.qora.data.transaction.RewardShareTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.transaction.Transaction.TransactionType; import org.qora.transform.TransformationException; import org.qora.utils.Serialization; -public class ProxyForgingTransactionTransformer extends TransactionTransformer { +public class RewardShareTransactionTransformer extends TransactionTransformer { // Property lengths private static final int TARGET_LENGTH = ADDRESS_LENGTH; @@ -25,14 +25,14 @@ public class ProxyForgingTransactionTransformer extends TransactionTransformer { static { layout = new TransactionLayout(); - layout.add("txType: " + TransactionType.PROXY_FORGING.valueString, TransformationType.INT); + layout.add("txType: " + TransactionType.REWARD_SHARE.valueString, TransformationType.INT); layout.add("timestamp", TransformationType.TIMESTAMP); layout.add("transaction's groupID", TransformationType.INT); layout.add("reference", TransformationType.SIGNATURE); - layout.add("forger's public key", TransformationType.PUBLIC_KEY); + layout.add("minter's public key", TransformationType.PUBLIC_KEY); layout.add("recipient account's address", TransformationType.ADDRESS); - layout.add("proxy's public key", TransformationType.PUBLIC_KEY); - layout.add("recipient's share of block rewards", TransformationType.AMOUNT); + layout.add("reward-share public key", TransformationType.PUBLIC_KEY); + layout.add("recipient's percentage share of block rewards", TransformationType.AMOUNT); layout.add("fee", TransformationType.AMOUNT); layout.add("signature", TransformationType.SIGNATURE); } @@ -47,22 +47,22 @@ public class ProxyForgingTransactionTransformer extends TransactionTransformer { byte[] reference = new byte[REFERENCE_LENGTH]; byteBuffer.get(reference); - byte[] forgerPublicKey = Serialization.deserializePublicKey(byteBuffer); + byte[] minterPublicKey = Serialization.deserializePublicKey(byteBuffer); String recipient = Serialization.deserializeAddress(byteBuffer); - byte[] proxyPublicKey = Serialization.deserializePublicKey(byteBuffer); + byte[] rewardSharePublicKey = Serialization.deserializePublicKey(byteBuffer); - BigDecimal share = Serialization.deserializeBigDecimal(byteBuffer); + BigDecimal sharePercent = Serialization.deserializeBigDecimal(byteBuffer); BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer); byte[] signature = new byte[SIGNATURE_LENGTH]; byteBuffer.get(signature); - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, forgerPublicKey, fee, signature); + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, minterPublicKey, fee, signature); - return new ProxyForgingTransactionData(baseTransactionData, recipient, proxyPublicKey, share); + return new RewardShareTransactionData(baseTransactionData, recipient, rewardSharePublicKey, sharePercent); } public static int getDataLength(TransactionData transactionData) throws TransformationException { @@ -71,22 +71,22 @@ public class ProxyForgingTransactionTransformer extends TransactionTransformer { public static byte[] toBytes(TransactionData transactionData) throws TransformationException { try { - ProxyForgingTransactionData proxyForgingTransactionData = (ProxyForgingTransactionData) transactionData; + RewardShareTransactionData rewardShareTransactionData = (RewardShareTransactionData) transactionData; ByteArrayOutputStream bytes = new ByteArrayOutputStream(); transformCommonBytes(transactionData, bytes); - Serialization.serializeAddress(bytes, proxyForgingTransactionData.getRecipient()); + Serialization.serializeAddress(bytes, rewardShareTransactionData.getRecipient()); - bytes.write(proxyForgingTransactionData.getProxyPublicKey()); + bytes.write(rewardShareTransactionData.getRewardSharePublicKey()); - Serialization.serializeBigDecimal(bytes, proxyForgingTransactionData.getShare()); + Serialization.serializeBigDecimal(bytes, rewardShareTransactionData.getSharePercent()); - Serialization.serializeBigDecimal(bytes, proxyForgingTransactionData.getFee()); + Serialization.serializeBigDecimal(bytes, rewardShareTransactionData.getFee()); - if (proxyForgingTransactionData.getSignature() != null) - bytes.write(proxyForgingTransactionData.getSignature()); + if (rewardShareTransactionData.getSignature() != null) + bytes.write(rewardShareTransactionData.getSignature()); return bytes.toByteArray(); } catch (IOException | ClassCastException e) { diff --git a/src/main/resources/blockchain.json b/src/main/resources/blockchain.json index a4de4015..b04f6417 100644 --- a/src/main/resources/blockchain.json +++ b/src/main/resources/blockchain.json @@ -8,32 +8,10 @@ "requireGroupForApproval": false, "defaultGroupId": 0, "oneNamePerAccount": true, - "maxProxyRelationships": 20, + "minAccountLevelToRewardShare": 5, + "maxRewardSharesPerMintingAccount": 20, "onlineAccountSignaturesMinLifetime": 2592000000, "onlineAccountSignaturesMaxLifetime": 3196800000, - "genesisInfo": { - "version": 4, - "timestamp": "1569510000000", - "transactions": [ - { "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORT", "description": "QORTAL coin", "quantity": 10000000, "isDivisible": true, "reference": "28u54WRcMfGujtQMZ9dNKFXVqucY7XfPihXAqPFsnx853NPUwfDJy1sMH5boCkahFgjUNYqc5fkduxdBhQTKgUsC", "data": "{}" }, - { "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORA", "description": "Representative legacy QORA", "quantity": 10000000000, "isDivisible": true, "data": "{}", "isUnspendable": true }, - { "type": "GENESIS", "recipient": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "amount": "1000000" }, - { "type": "GENESIS", "recipient": "QcatoCyyp7dVfMtJ92sgUUPDoBJevaemRX", "amount": "1000000" }, - { "type": "GENESIS", "recipient": "QTiga19sttbf6CLQLT83mhCSWEaCvjk8th", "amount": "1000000" }, - { "type": "GENESIS", "recipient": "QcrowX39FuycKvMFFBsakyd5HSxe7bxFsn", "amount": "1000000" }, - { "type": "ACCOUNT_FLAGS", "target": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "andMask": -1, "orMask": 1, "xorMask": 0 }, - { "type": "ACCOUNT_FLAGS", "target": "QcatoCyyp7dVfMtJ92sgUUPDoBJevaemRX", "andMask": -1, "orMask": 1, "xorMask": 0 }, - { "type": "ACCOUNT_FLAGS", "target": "QTiga19sttbf6CLQLT83mhCSWEaCvjk8th", "andMask": -1, "orMask": 1, "xorMask": 0 }, - { "type": "ACCOUNT_FLAGS", "target": "QcrowX39FuycKvMFFBsakyd5HSxe7bxFsn", "andMask": -1, "orMask": 1, "xorMask": 0 }, - { "type": "ACCOUNT_LEVEL", "target": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "level": 8 }, - { "type": "ACCOUNT_LEVEL", "target": "QcatoCyyp7dVfMtJ92sgUUPDoBJevaemRX", "level": 3 }, - { "type": "ACCOUNT_LEVEL", "target": "QTiga19sttbf6CLQLT83mhCSWEaCvjk8th", "level": 10 }, - { "type": "ACCOUNT_LEVEL", "target": "QcrowX39FuycKvMFFBsakyd5HSxe7bxFsn", "level": 10 }, - { "type": "CREATE_GROUP", "creatorPublicKey": "6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb", "owner": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT60", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 }, - { "type": "CREATE_GROUP", "creatorPublicKey": "JBNBQQDzZsm5do1BrwWAp53Ps4KYJVt749EGpCf7ofte", "owner": "QTiga19sttbf6CLQLT83mhCSWEaCvjk8th", "groupName": "Tiga", "description": "Tiga's group", "isOpen": true, "approvalThreshold": "PCT20", "minimumBlockDelay": 120, "maximumBlockDelay": 2880 }, - { "type": "PROXY_FORGING", "forgerPublicKey": "6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb", "recipient": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "proxyPublicKey": "8X3w1521UNnnonieugAxhfbfvqoRpwPXJrwGQZb5JjQ3", "share": 100 } - ] - }, "rewardsByHeight": [ { "height": 2, "reward": 5.0000 }, { "height": 259204, "reward": 4.7500 }, @@ -71,5 +49,117 @@ "v2Timestamp": 0, "newAssetPricingTimestamp": 0, "groupApprovalTimestamp": 0 + }, + "genesisInfo": { + "version": 4, + "timestamp": "1572000000000", + "transactions": [ + { "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORT", "description": "QORTAL coin", "quantity": 0, "isDivisible": true, "reference": "28u54WRcMfGujtQMZ9dNKFXVqucY7XfPihXAqPFsnx853NPUwfDJy1sMH5boCkahFgjUNYqc5fkduxdBhQTKgUsC", "data": "{}" }, + { "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true }, + + { "type": "GENESIS", "recipient": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "amount": "1000" }, + { "type": "ACCOUNT_FLAGS", "target": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "andMask": -1, "orMask": 1, "xorMask": 0 }, + { "type": "ACCOUNT_LEVEL", "target": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "level": 8 }, + { "type": "REWARD_SHARE", "minterPublicKey": "6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb", "recipient": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "rewardSharePublicKey": "8X3w1521UNnnonieugAxhfbfvqoRpwPXJrwGQZb5JjQ3", "sharePercent": 100 }, + + { "type": "GENESIS", "recipient": "QcatoCyyp7dVfMtJ92sgUUPDoBJevaemRX", "amount": "1000" }, + { "type": "ACCOUNT_FLAGS", "target": "QcatoCyyp7dVfMtJ92sgUUPDoBJevaemRX", "andMask": -1, "orMask": 1, "xorMask": 0 }, + { "type": "ACCOUNT_LEVEL", "target": "QcatoCyyp7dVfMtJ92sgUUPDoBJevaemRX", "level": 3 }, + + { "type": "GENESIS", "recipient": "QTiga19sttbf6CLQLT83mhCSWEaCvjk8th", "amount": "1000" }, + { "type": "ACCOUNT_FLAGS", "target": "QTiga19sttbf6CLQLT83mhCSWEaCvjk8th", "andMask": -1, "orMask": 1, "xorMask": 0 }, + { "type": "ACCOUNT_LEVEL", "target": "QTiga19sttbf6CLQLT83mhCSWEaCvjk8th", "level": 10 }, + + { "type": "GENESIS", "recipient": "QcrowX39FuycKvMFFBsakyd5HSxe7bxFsn", "amount": "1000" }, + { "type": "ACCOUNT_FLAGS", "target": "QcrowX39FuycKvMFFBsakyd5HSxe7bxFsn", "andMask": -1, "orMask": 1, "xorMask": 0 }, + { "type": "ACCOUNT_LEVEL", "target": "QcrowX39FuycKvMFFBsakyd5HSxe7bxFsn", "level": 10 }, + + { "type": "CREATE_GROUP", "creatorPublicKey": "6rNn9b3pYRrG9UKqzMWYZ9qa8F3Zgv2mVWrULGHUusb", "owner": "QcatTpaU1UneBs3fVHo8QN6mUmuceRVzFY", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT60", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 }, + + { "type": "ACCOUNT_LEVEL", "target": "QQKeokRiFCgAhBSdu1DUf5e1LCkgApvrxZ", "level": 5 }, + { "type": "ACCOUNT_LEVEL", "target": "QiaaoNZ54wKoaUMXxW72UsPt1MiPpeUTWm", "level": 5 }, + { "type": "ACCOUNT_LEVEL", "target": "QN5XF1YQUyVt3S1LNZtStXQCbtxyhkj2FR", "level": 5 }, + { "type": "ACCOUNT_LEVEL", "target": "QYoRWAxw6CVMeYeWHKJh3csmTVkVzjpdBo", "level": 5 }, + { "type": "ACCOUNT_LEVEL", "target": "QMtx2UmUuRZckCmRJRyxdzSAazHP8hU5rA", "level": 4 }, + { "type": "ACCOUNT_LEVEL", "target": "QM4LF9EzQnXJ9VyFwnJbJzskJLrybuSzsw", "level": 3 }, + { "type": "ACCOUNT_LEVEL", "target": "QMkgf9Y6Ac2TUrynDvyhX69ekpC3P3GQmN", "level": 3 }, + { "type": "ACCOUNT_LEVEL", "target": "QiRey96Ka6uWrHTvSn3zY7cveqj3Et3YQd", "level": 2 }, + { "type": "ACCOUNT_LEVEL", "target": "QjchrhQ6HPhWn1sAr68PfYdeMS3C6PjH2J", "level": 2 }, + { "type": "ACCOUNT_LEVEL", "target": "QMWSbqWDC1eHU7fCGJ3UwABHiTxBgBJGRC", "level": 2 }, + { "type": "ACCOUNT_LEVEL", "target": "QWbLcR7ijdJV3eU23wVKk3gKh7G6wKEj3d", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QWviNvp1KPMdyG83ZB2HH29Xxca8fCAv9D", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QRbxbqavJNQYAhqygxYnbrbFx3LmfuTehe", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QLpqg45nszcs1ewfGrqcXzQX8VuZCaxbN6", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QSqeotdyTHxrpx4uquPfq4LLBmezBDuCBo", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "Qa1DSUZPXQSnC1DaTdpZ1fBaCuS62F8GLM", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QS2k8PMpmvUHzFDp2JfbVxRo8SiGiV72xx", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QaYAUoT2SzYA6kqBEW6W11brkYpSf7g4Wy", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QLrErVJvpRdYAt4dGwk8i8rG3PMUSgpp3i", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QQLnurs5NkGB59Ww1jrnRM6deJbAahVLt4", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QczBn7uYPb3Evsei2AVjMqSpmV5f2e94Hf", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QfnYhVqbi8Co87dpzqB8NUXxoRhrcQ55am", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QPR9YBXhyAaCZKhvYK6Z9K9ddBM8F7kT21", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QPa2Jy6KK1Yf9XnETHSW5Rp9Pwjum2VWBP", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QQaEZ52y8govJUUTDdQtERU9saG9kg491N", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QQShonrAdJXg4CzhegtynzaBP7S631vKGq", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QdxuYD638XE84UGdKnb8QPoss1shkMRKYw", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QWGwGBzZG8UeNXJYZq9tjzN6ceriqTgMRd", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QaWReiLe5tAsTNoq7hMiSGNasJEXTX5bmn", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QjJjjBUJSZAMuYiwTyfJTFthH6SrofjG6d", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QQSiZAgdMjadXZRVTHa252AAY2yjZfQKR1", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QbdHzyw6q2hMAhg12JFbXSEcbrj43xNnYG", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QRD3WHcsek8TB9ReSQqGAAYopBwZm1Ue8C", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QQMswzmv65MZSdafqzCACm3tbLj23ixkVR", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QM5Fg3kBzj9BqsGAJKHhsEtgRgf4x2Yn5G", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QNwedkYs6hToBRPZvBb6naKvDEgB7BeaKi", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QPV6E7UXYjcxxFECoiRfwK8bU9AxaMH36U", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QPK8TJ5t32YWef7UCJErELtqKXy2aKPmqy", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QQ2ijixLLGg6Bic5ho1Q1uM5nDdN7PEujQ", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QLgBTDkt7t7WwbTd1RM8iGxu9FexPZcn7C", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "Qa15fMdEiY7WqK2xwvdHrdhU2TE6yk3V3L", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QVxvbaNRk2LUheWJaAzS3dpZ3iSFwrd8Jo", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QVz1rKM9QQrGX7gkxR9EV8a2T4AuD1QT87", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QiECCjPphHZNBbpbqgzRuDnBqbYTH6sBCN", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QU8SzA12SVKPgqpTzRna5itKktGhvEdmgP", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QMz7UGfYpjBVfgAhqdKEMAkFyyGi4CrDc2", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QRh9zNPmz5McxeWx3iCy2xnBRrBoYDZfMJ", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QNyEBd1MNtY8itADG6WQHXSEJWV5dvTLf5", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QLcvvSBPkLkfz9rSKATf7DmS6M2ryw7okL", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QhUWSWWFt6vDy2qNFn68JPTPLjyDrzrh4D", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QUNZQwRXhu1h5fXh39EgbQqCCN1gNo8MV6", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QPow3AFucRRHyNP8csLspMJL9N3hKYiKGK", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QVddqTw2VyqtP91F3U21EiMwov7tfuNsQd", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QPUyMFyXB6g3uUkUruy2AVhrmBniZvHkQ1", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QP86QhNUhLKUignCEo2MFmj7XPFEq1KMZ5", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QNsdyTKN7MVsTYqSyZNPatzRTgNyWDqPZW", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "Qa95hURaNK4kPhDhbdmDFm2wMkkoWFZ4Zz", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QZNYeoAtuSGajD3kihKUBFNv9TvJ6cFqru", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QUnfm6t9SrhgFB8YqCV1vQhmqFzhncxioK", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QRy5dV1xgfxMFXuKHHke1SiFQcdAGkYpnT", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QeaPN9sxEXR5EJ71wuXmWj63XN9MDToD4w", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QTekAqCoXPy5R7NE78GXxjSaXBuekZge88", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QMFJGxGLCbegHL3NZ7HRo3UYaCXhweJVKS", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QSZtc5EQBuVcovMBxcdSQNnEqwdUZvzjSe", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QNFWWhmsun3HVUChjck8XqhsQW2tckNxxM", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QYsLsfwMRBPnunmuWmFkM4hvGsfooY8ssU", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QW9Gf4VofYEyqgxBGzbPZuqcozY2uaMNBc", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QNzsigxC8EikCFWCun9cieggFtbJDz6f3Q", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QR3fQyu3g3ox6mGoyC9ytaVKZwugmbmSFh", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QMvymCURS2abuV1Gn8tFiMkePpvXTKacco", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QVD1zDHyNy3PWgcsuYMz1NqzKGMiVrXD2T", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "Qiw4VrpLh5yx9RH1VbL9jrKhz7S7sHztDw", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QUkq2151QPXfvt8VPc7u5LjFhiHgAtEvC9", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QRHDHASWAXarqTvB2X4SNtJCWbxGf68M2o", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QR9e8PfrLWU96QCL4UQNqioEUcAGoHfHBH", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QPZTxWtCmH6Y6zwwntjnPDfKG6zNKRivqJ", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QMZwrMVDW8L5CTb896ubMeRWUBjiG4cQAi", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QMYzatepF38RJwe3UygUXEh4sA3K7HoQfG", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QMNdPz11XubtvxXLGeiG3PHKaQW67LkZMp", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QM7KP9J7ty7gVAg13DXJyEkASiWmdT5GQC", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QjhFQysNr2R4fuybiS6Nm6tPvPhT8xcmqF", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QixvmAGdJWNkSmentXe8CAnisU9DmifB1C", "level": 1 }, + { "type": "ACCOUNT_LEVEL", "target": "QgrEf4nHhijFQCVgJKro8cRgYWAy1G5rDg", "level": 1 } + + ] } } diff --git a/src/main/resources/i18n/SysTray_en.properties b/src/main/resources/i18n/SysTray_en.properties index 63fa3420..e087a08e 100644 --- a/src/main/resources/i18n/SysTray_en.properties +++ b/src/main/resources/i18n/SysTray_en.properties @@ -11,9 +11,9 @@ CONNECTIONS = connections EXIT = Exit -GENERATING_DISABLED = NOT minting +MINTING_DISABLED = NOT minting -GENERATING_ENABLED = \u2714 Minting +MINTING_ENABLED = \u2714 Minting # Nagging about lack of NTP time sync NTP_NAG_CAPTION = Computer's clock is inaccurate! diff --git a/src/main/resources/i18n/SysTray_zh.properties b/src/main/resources/i18n/SysTray_zh.properties index 4f2946b5..587b4302 100644 --- a/src/main/resources/i18n/SysTray_zh.properties +++ b/src/main/resources/i18n/SysTray_zh.properties @@ -11,9 +11,9 @@ CONNECTIONS = \u4E2A\u8FDE\u63A5 EXIT = \u9000\u51FA\u8F6F\u4EF6 -GENERATING_DISABLED = \u6CA1\u6709\u94F8\u5E01 +MINTING_DISABLED = \u6CA1\u6709\u94F8\u5E01 -GENERATING_ENABLED = \u2714 \u94F8\u5E01 +MINTING_ENABLED = \u2714 \u94F8\u5E01 # Nagging about lack of NTP time sync NTP_NAG_CAPTION = \u7535\u8111\u7684\u65F6\u949F\u4E0D\u51C6\u786E\uFF01 diff --git a/src/test/java/org/qora/test/BTCACCTTests.java b/src/test/java/org/qora/test/BTCACCTTests.java index 943d6b96..f83d3c2c 100644 --- a/src/test/java/org/qora/test/BTCACCTTests.java +++ b/src/test/java/org/qora/test/BTCACCTTests.java @@ -31,8 +31,6 @@ import org.bitcoinj.script.ScriptChunk; import org.bitcoinj.script.ScriptOpCodes; import org.bitcoinj.wallet.WalletTransaction.Pool; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.junit.BeforeClass; -import org.junit.Test; import com.google.common.hash.HashCode; import com.google.common.primitives.Bytes; @@ -76,13 +74,9 @@ public class BTCACCTTests { private static final boolean doRefundNotRedeem = false; - @BeforeClass - public static void beforeClass() { + public void main(String[] args) throws NoSuchAlgorithmException, InsufficientMoneyException, InterruptedException, ExecutionException, UnknownHostException { Security.insertProviderAt(new BouncyCastleProvider(), 0); - } - @Test - public void buildBTCACCTTest() throws NoSuchAlgorithmException, InsufficientMoneyException, InterruptedException, ExecutionException, UnknownHostException { byte[] secret = new byte[32]; new SecureRandom().nextBytes(secret); diff --git a/src/test/java/org/qora/test/BlockTests.java b/src/test/java/org/qora/test/BlockTests.java index 686249bc..72fa89ba 100644 --- a/src/test/java/org/qora/test/BlockTests.java +++ b/src/test/java/org/qora/test/BlockTests.java @@ -7,7 +7,7 @@ import org.junit.Before; import org.junit.Test; import org.qora.account.PrivateKeyAccount; import org.qora.block.Block; -import org.qora.block.BlockGenerator; +import org.qora.block.BlockMinter; import org.qora.block.GenesisBlock; import org.qora.data.at.ATStateData; import org.qora.data.block.BlockData; @@ -69,7 +69,7 @@ public class BlockTests extends Common { assertEquals(Transaction.TransactionType.GENESIS, transactionData.getType()); assertTrue(transactionData.getFee().compareTo(BigDecimal.ZERO) == 0); - assertNull(transactionData.getReference()); + // assertNull(transactionData.getReference()); Transaction transaction = Transaction.fromData(repository, transactionData); assertNotNull(transaction); @@ -106,7 +106,7 @@ public class BlockTests extends Common { } catch (InterruptedException e) { } - BlockGenerator.generateTestingBlock(repository, signingAccount); + BlockMinter.mintTestingBlock(repository, signingAccount); BlockData blockData = repository.getBlockRepository().getLastBlock(); Block block = new Block(repository, blockData); diff --git a/src/test/java/org/qora/test/ChainWeightTests.java b/src/test/java/org/qora/test/ChainWeightTests.java index 2e56c52f..48016675 100644 --- a/src/test/java/org/qora/test/ChainWeightTests.java +++ b/src/test/java/org/qora/test/ChainWeightTests.java @@ -44,7 +44,7 @@ public class ChainWeightTests { } private static BigInteger calcBlockWeight(int parentHeight, byte[] parentGeneratorKey, BlockSummaryData blockSummaryData) { - BigInteger keyDistance = calcKeyDistance(parentHeight, parentGeneratorKey, blockSummaryData.getGeneratorPublicKey()); + BigInteger keyDistance = calcKeyDistance(parentHeight, parentGeneratorKey, blockSummaryData.getMinterPublicKey()); BigInteger weight = BigInteger.valueOf(blockSummaryData.getOnlineAccountsCount()).shiftLeft(ACCOUNTS_COUNT_SHIFT).add(keyDistance); return weight; } @@ -57,7 +57,7 @@ public class ChainWeightTests { for (BlockSummaryData blockSummaryData : blockSummaries) { cumulativeWeight = cumulativeWeight.shiftLeft(CHAIN_WEIGHT_SHIFT).add(calcBlockWeight(parentHeight, parentGeneratorKey, blockSummaryData)); parentHeight = blockSummaryData.getHeight(); - parentGeneratorKey = blockSummaryData.getGeneratorPublicKey(); + parentGeneratorKey = blockSummaryData.getMinterPublicKey(); } return cumulativeWeight; @@ -120,7 +120,7 @@ public class ChainWeightTests { public void testLongerChain() { final int commonBlockHeight = 1; BlockSummaryData commonBlockSummary = genBlockSummary(commonBlockHeight); - byte[] commonBlockGeneratorKey = commonBlockSummary.getGeneratorPublicKey(); + byte[] commonBlockGeneratorKey = commonBlockSummary.getMinterPublicKey(); List shorterChain = genBlockSummaries(3, commonBlockSummary); List longerChain = genBlockSummaries(shorterChain.size() + 1, commonBlockSummary); diff --git a/src/test/java/org/qora/test/CiyamMemoryPoWTests.java b/src/test/java/org/qora/test/CiyamMemoryPoWTests.java new file mode 100644 index 00000000..e2e99cac --- /dev/null +++ b/src/test/java/org/qora/test/CiyamMemoryPoWTests.java @@ -0,0 +1,40 @@ +package org.qora.test; + +import org.junit.Test; +import org.qora.crypto.CiyamMemoryPoW; + +import static org.junit.Assert.*; + +import java.util.Random; + +public class CiyamMemoryPoWTests { + + @Test + public void testCompute() { + Random random = new Random(); + + byte[] data = new byte[256]; + random.nextBytes(data); + + int start = 0; + int range = 1000000; + int difficulty = 1; + + long startTime = System.currentTimeMillis(); + Integer nonce = CiyamMemoryPoW.compute(data, start, range, difficulty); + long finishTime = System.currentTimeMillis(); + + System.out.println(String.format("Memory-hard PoW took %dms", finishTime - startTime)); + + assertNotNull(nonce); + + System.out.println(String.format("nonce: %d", nonce)); + } + + @Test + public void testMultipleComputes() { + for (int i = 0; i < 10; ++i) + testCompute(); + } + +} diff --git a/src/test/java/org/qora/test/CompatibilityTests.java b/src/test/java/org/qora/test/CompatibilityTests.java index 6fbafcb2..34167a5f 100644 --- a/src/test/java/org/qora/test/CompatibilityTests.java +++ b/src/test/java/org/qora/test/CompatibilityTests.java @@ -2,17 +2,28 @@ package org.qora.test; import org.junit.Test; import org.qora.data.transaction.TransactionData; +import org.qora.repository.DataException; +import org.qora.test.common.Common; import org.qora.transaction.CreateAssetOrderTransaction; import org.qora.transaction.CreatePollTransaction; import org.qora.transaction.IssueAssetTransaction; import org.qora.transform.TransformationException; import org.qora.transform.transaction.TransactionTransformer; +import org.qora.utils.NTP; import static org.junit.Assert.*; +import org.junit.Before; + import com.google.common.hash.HashCode; -public class CompatibilityTests { +public class CompatibilityTests extends Common { + + @Before + public void beforeTest() throws DataException { + Common.useSettings("test-settings-v1.json"); + NTP.testMode(); + } @Test public void testCreateOrderTransactionSignature() throws TransformationException { diff --git a/src/test/java/org/qora/test/CryptoTests.java b/src/test/java/org/qora/test/CryptoTests.java index 94690814..7869ed03 100644 --- a/src/test/java/org/qora/test/CryptoTests.java +++ b/src/test/java/org/qora/test/CryptoTests.java @@ -250,7 +250,7 @@ public class CryptoTests extends Common { final String expectedProxyPrivateKey = "6KszntmNuXmpUkzLfuttgMPeownctxrnyZUG9rErKJJx"; PrivateKeyAccount mintingAccount = new PrivateKeyAccount(null, ourPrivateKey); - byte[] proxyPrivateKey = mintingAccount.getProxyPrivateKey(theirPublicKey); + byte[] proxyPrivateKey = mintingAccount.getRewardSharePrivateKey(theirPublicKey); assertEquals(expectedProxyPrivateKey, Base58.encode(proxyPrivateKey)); } diff --git a/src/test/java/org/qora/test/GuiTests.java b/src/test/java/org/qora/test/GuiTests.java index 5e8cdb26..5a84a6ce 100644 --- a/src/test/java/org/qora/test/GuiTests.java +++ b/src/test/java/org/qora/test/GuiTests.java @@ -1,5 +1,7 @@ package org.qora.test; +import java.awt.TrayIcon.MessageType; + import org.junit.Test; import org.qora.gui.SplashFrame; import org.qora.gui.SysTray; @@ -19,7 +21,11 @@ public class GuiTests { public void testSysTray() throws InterruptedException { SysTray.getInstance(); + SysTray.getInstance().showMessage("Testing...", "Tray icon should disappear in 10 seconds", MessageType.INFO); + Thread.sleep(10_000L); + + SysTray.getInstance().dispose(); } } diff --git a/src/test/java/org/qora/test/OnlineTests.java b/src/test/java/org/qora/test/OnlineTests.java deleted file mode 100644 index 3ab55307..00000000 --- a/src/test/java/org/qora/test/OnlineTests.java +++ /dev/null @@ -1,316 +0,0 @@ -package org.qora.test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Random; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import org.junit.Before; -import org.junit.Test; -import org.qora.account.PrivateKeyAccount; -import org.qora.account.PublicKeyAccount; -import org.qora.data.network.OnlineAccountData; -import org.qora.network.message.GetOnlineAccountsMessage; -import org.qora.network.message.Message; -import org.qora.network.message.OnlineAccountsMessage; -import org.qora.repository.DataException; -import org.qora.test.common.Common; -import org.qora.test.common.FakePeer; -import org.qora.utils.ByteArray; - -import com.google.common.primitives.Longs; - -public class OnlineTests extends Common { - - @Before - public void beforeTest() throws DataException { - Common.useDefaultSettings(); - } - - private static final int MAX_PEERS = 100; - private static final int MAX_RUNNING_PEERS = 20; - private static final boolean LOG_CONNECTION_CHANGES = false; - private static final boolean LOG_ACCOUNT_CHANGES = true; - private static final boolean GET_ONLINE_UNICAST_NOT_BROADCAST = false; - private static final long ONLINE_TIMESTAMP_MODULUS = 5 * 60 * 1000; - - private static List allKnownAccounts; - private static final Random random = new Random(); - - static class OnlinePeer extends FakePeer { - private static final long LAST_SEEN_EXPIRY_PERIOD = 6 * 60 * 1000; - private static final long ONLINE_REFRESH_INTERVAL = 4 * 60 * 1000; - private static final int MAX_CONNECTED_PEERS = 5; - - private final PrivateKeyAccount account; - - private List onlineAccounts; - private long nextOnlineRefresh = 0; - - public OnlinePeer(int id, PrivateKeyAccount account) { - super(id); - - this.account = account; - - this.onlineAccounts = Collections.synchronizedList(new ArrayList<>()); - } - - @Override - protected void processMessage(FakePeer peer, Message message) throws InterruptedException { - switch (message.getType()) { - case GET_ONLINE_ACCOUNTS: { - GetOnlineAccountsMessage getOnlineAccountsMessage = (GetOnlineAccountsMessage) message; - - List excludeAccounts = getOnlineAccountsMessage.getOnlineAccounts(); - - // Send online accounts info, excluding entries with matching timestamp & public key from excludeAccounts - List accountsToSend; - synchronized (this.onlineAccounts) { - accountsToSend = new ArrayList<>(this.onlineAccounts); - } - - Iterator iterator = accountsToSend.iterator(); - - SEND_ITERATOR: - while (iterator.hasNext()) { - OnlineAccountData onlineAccount = iterator.next(); - - for (int i = 0; i < excludeAccounts.size(); ++i) { - OnlineAccountData excludeAccount = excludeAccounts.get(i); - - if (onlineAccount.getTimestamp() == excludeAccount.getTimestamp() && Arrays.equals(onlineAccount.getPublicKey(), excludeAccount.getPublicKey())) { - iterator.remove(); - continue SEND_ITERATOR; - } - } - } - - Message onlineAccountsMessage = new OnlineAccountsMessage(accountsToSend); - this.send(peer, onlineAccountsMessage); - - if (LOG_ACCOUNT_CHANGES) - System.out.println(String.format("[%d] sent %d of our %d online accounts to %d", this.getId(), accountsToSend.size(), onlineAccounts.size(), peer.getId())); - - break; - } - - case ONLINE_ACCOUNTS: { - OnlineAccountsMessage onlineAccountsMessage = (OnlineAccountsMessage) message; - - List onlineAccounts = onlineAccountsMessage.getOnlineAccounts(); - - if (LOG_ACCOUNT_CHANGES) - System.out.println(String.format("[%d] received %d online accounts from %d", this.getId(), onlineAccounts.size(), peer.getId())); - - for (OnlineAccountData onlineAccount : onlineAccounts) - verifyAndAddAccount(onlineAccount); - - break; - } - - default: - break; - } - } - - private void verifyAndAddAccount(OnlineAccountData onlineAccount) { - // we would check timestamp is 'recent' here - - // Verify - byte[] data = Longs.toByteArray(onlineAccount.getTimestamp()); - PublicKeyAccount otherAccount = new PublicKeyAccount(null, onlineAccount.getPublicKey()); - if (!otherAccount.verify(onlineAccount.getSignature(), data)) { - System.out.println(String.format("[%d] rejecting invalid online account %s", this.getId(), otherAccount.getAddress())); - return; - } - - ByteArray publicKeyBA = new ByteArray(onlineAccount.getPublicKey()); - - synchronized (this.onlineAccounts) { - OnlineAccountData existingAccount = this.onlineAccounts.stream().filter(account -> new ByteArray(account.getPublicKey()).equals(publicKeyBA)).findFirst().orElse(null); - - if (existingAccount != null) { - if (existingAccount.getTimestamp() < onlineAccount.getTimestamp()) { - this.onlineAccounts.remove(existingAccount); - - if (LOG_ACCOUNT_CHANGES) - System.out.println(String.format("[%d] updated online account %s with timestamp %d (was %d)", this.getId(), otherAccount.getAddress(), onlineAccount.getTimestamp(), existingAccount.getTimestamp())); - } else { - if (LOG_ACCOUNT_CHANGES) - System.out.println(String.format("[%d] ignoring existing online account %s", this.getId(), otherAccount.getAddress())); - - return; - } - } else { - if (LOG_ACCOUNT_CHANGES) - System.out.println(String.format("[%d] added online account %s with timestamp %d", this.getId(), otherAccount.getAddress(), onlineAccount.getTimestamp())); - } - - this.onlineAccounts.add(onlineAccount); - } - } - - @Override - protected void performIdleTasks() { - final long now = System.currentTimeMillis(); - - // Expire old entries - final long cutoffThreshold = now - LAST_SEEN_EXPIRY_PERIOD; - synchronized (this.onlineAccounts) { - Iterator iterator = this.onlineAccounts.iterator(); - while (iterator.hasNext()) { - OnlineAccountData onlineAccount = iterator.next(); - - if (onlineAccount.getTimestamp() < cutoffThreshold) { - iterator.remove(); - - if (LOG_ACCOUNT_CHANGES) { - PublicKeyAccount otherAccount = new PublicKeyAccount(null, onlineAccount.getPublicKey()); - System.out.println(String.format("[%d] removed expired online account %s with timestamp %d", this.getId(), otherAccount.getAddress(), onlineAccount.getTimestamp())); - } - } - } - } - - // Request data from another peer - Message message; - synchronized (this.onlineAccounts) { - message = new GetOnlineAccountsMessage(this.onlineAccounts); - } - - if (GET_ONLINE_UNICAST_NOT_BROADCAST) { - FakePeer peer = this.pickRandomPeer(); - if (peer != null) - this.send(peer, message); - } else { - this.broadcast(message); - } - - // Refresh our onlineness? - if (now >= this.nextOnlineRefresh) { - this.nextOnlineRefresh = now + ONLINE_REFRESH_INTERVAL; - refreshOnlineness(); - } - - // Log our online list - synchronized (this.onlineAccounts) { - System.out.println(String.format("[%d] Connections: %d, online accounts: %d", this.getId(), this.peers.size(), this.onlineAccounts.size())); - } - } - - private void refreshOnlineness() { - // Broadcast signed timestamp - final long timestamp = (System.currentTimeMillis() / ONLINE_TIMESTAMP_MODULUS) * ONLINE_TIMESTAMP_MODULUS; - - byte[] data = Longs.toByteArray(timestamp); - byte[] signature = this.account.sign(data); - byte[] publicKey = this.account.getPublicKey(); - - // Our account is online - OnlineAccountData onlineAccount = new OnlineAccountData(timestamp, signature, publicKey); - synchronized (this.onlineAccounts) { - this.onlineAccounts.removeIf(account -> account.getPublicKey() == this.account.getPublicKey()); - this.onlineAccounts.add(onlineAccount); - } - - Message message = new OnlineAccountsMessage(Arrays.asList(onlineAccount)); - this.broadcast(message); - - if (LOG_ACCOUNT_CHANGES) - System.out.println(String.format("[%d] broadcasted online account %s with timestamp %d", this.getId(), this.account.getAddress(), timestamp)); - } - - @Override - public void connect(FakePeer otherPeer) { - int totalPeers; - synchronized (this.peers) { - totalPeers = this.peers.size(); - } - - if (totalPeers >= MAX_CONNECTED_PEERS) - return; - - super.connect(otherPeer); - - if (LOG_CONNECTION_CHANGES) - System.out.println(String.format("[%d] Connected to peer %d, total peers: %d", this.getId(), otherPeer.getId(), totalPeers + 1)); - } - - public void randomDisconnect() { - FakePeer peer; - int totalPeers; - - synchronized (this.peers) { - peer = this.pickRandomPeer(); - if (peer == null) - return; - - totalPeers = this.peers.size(); - } - - this.disconnect(peer); - - if (LOG_CONNECTION_CHANGES) - System.out.println(String.format("[%d] Disconnected peer %d, total peers: %d", this.getId(), peer.getId(), totalPeers - 1)); - } - } - - @Test - public void testOnlineness() throws InterruptedException { - allKnownAccounts = new ArrayList<>(); - - List allPeers = new ArrayList<>(); - - for (int i = 0; i < MAX_PEERS; ++i) { - byte[] seed = new byte[32]; - random.nextBytes(seed); - PrivateKeyAccount account = new PrivateKeyAccount(null, seed); - - allKnownAccounts.add(account); - - OnlinePeer peer = new OnlinePeer(i, account); - allPeers.add(peer); - } - - // Start up some peers - List runningPeers = new ArrayList<>(); - ExecutorService peerExecutor = Executors.newCachedThreadPool(); - - for (int c = 0; c < MAX_RUNNING_PEERS; ++c) { - OnlinePeer newPeer; - do { - int i = random.nextInt(allPeers.size()); - newPeer = allPeers.get(i); - } while (runningPeers.contains(newPeer)); - - runningPeers.add(newPeer); - peerExecutor.execute(newPeer); - } - - // Randomly connect/disconnect peers - while (true) { - int i = random.nextInt(runningPeers.size()); - OnlinePeer peer = runningPeers.get(i); - - if ((random.nextInt() & 0xf) != 0) { - // Connect - OnlinePeer otherPeer; - do { - int j = random.nextInt(runningPeers.size()); - otherPeer = runningPeers.get(j); - } while (otherPeer == peer); - - peer.connect(otherPeer); - } else { - peer.randomDisconnect(); - } - - Thread.sleep(100); - } - } - -} diff --git a/src/test/java/org/qora/test/api/AddressesApiTests.java b/src/test/java/org/qora/test/api/AddressesApiTests.java index 961ab28b..72fa500f 100644 --- a/src/test/java/org/qora/test/api/AddressesApiTests.java +++ b/src/test/java/org/qora/test/api/AddressesApiTests.java @@ -24,13 +24,13 @@ public class AddressesApiTests extends ApiCommon { } @Test - public void testGetProxying() { - assertNotNull(this.addressesResource.getProxying(Collections.singletonList(aliceAddress), null, null, null, null, null)); - assertNotNull(this.addressesResource.getProxying(null, Collections.singletonList(aliceAddress), null, null, null, null)); - assertNotNull(this.addressesResource.getProxying(Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), null, null, null, null)); - assertNotNull(this.addressesResource.getProxying(null, null, Collections.singletonList(aliceAddress), null, null, null)); - assertNotNull(this.addressesResource.getProxying(Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), null, null, null)); - assertNotNull(this.addressesResource.getProxying(Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), 1, 1, true)); + public void testGetRewardShares() { + assertNotNull(this.addressesResource.getRewardShares(Collections.singletonList(aliceAddress), null, null, null, null, null)); + assertNotNull(this.addressesResource.getRewardShares(null, Collections.singletonList(aliceAddress), null, null, null, null)); + assertNotNull(this.addressesResource.getRewardShares(Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), null, null, null, null)); + assertNotNull(this.addressesResource.getRewardShares(null, null, Collections.singletonList(aliceAddress), null, null, null)); + assertNotNull(this.addressesResource.getRewardShares(Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), null, null, null)); + assertNotNull(this.addressesResource.getRewardShares(Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), Collections.singletonList(aliceAddress), 1, 1, true)); } } diff --git a/src/test/java/org/qora/test/api/BlockApiTests.java b/src/test/java/org/qora/test/api/BlockApiTests.java index 6f19f52e..dc0ac657 100644 --- a/src/test/java/org/qora/test/api/BlockApiTests.java +++ b/src/test/java/org/qora/test/api/BlockApiTests.java @@ -29,16 +29,16 @@ public class BlockApiTests extends ApiCommon { public void testGetBlockForgers() { List addresses = Arrays.asList(aliceAddress, aliceAddress); - assertNotNull(this.blocksResource.getBlockForgers(Collections.emptyList(), null, null, null)); - assertNotNull(this.blocksResource.getBlockForgers(addresses, null, null, null)); - assertNotNull(this.blocksResource.getBlockForgers(Collections.emptyList(), 1, 1, true)); - assertNotNull(this.blocksResource.getBlockForgers(addresses, 1, 1, true)); + assertNotNull(this.blocksResource.getBlockMinters(Collections.emptyList(), null, null, null)); + assertNotNull(this.blocksResource.getBlockMinters(addresses, null, null, null)); + assertNotNull(this.blocksResource.getBlockMinters(Collections.emptyList(), 1, 1, true)); + assertNotNull(this.blocksResource.getBlockMinters(addresses, 1, 1, true)); } @Test public void testGetBlocksByForger() { - assertNotNull(this.blocksResource.getBlocksByForger(aliceAddress, null, null, null)); - assertNotNull(this.blocksResource.getBlocksByForger(aliceAddress, 1, 1, true)); + assertNotNull(this.blocksResource.getBlocksByMinter(aliceAddress, null, null, null)); + assertNotNull(this.blocksResource.getBlocksByMinter(aliceAddress, 1, 1, true)); } } diff --git a/src/test/java/org/qora/test/assets/OldTradingTests.java b/src/test/java/org/qora/test/assets/OldTradingTests.java index c39efb2b..46c78815 100644 --- a/src/test/java/org/qora/test/assets/OldTradingTests.java +++ b/src/test/java/org/qora/test/assets/OldTradingTests.java @@ -10,6 +10,7 @@ import org.qora.repository.RepositoryManager; import org.qora.test.common.AccountUtils; import org.qora.test.common.AssetUtils; import org.qora.test.common.Common; +import org.qora.utils.NTP; import java.math.BigDecimal; import java.util.Map; @@ -19,6 +20,7 @@ public class OldTradingTests extends Common { @Before public void beforeTest() throws DataException { Common.useSettings("test-settings-old-asset.json"); + NTP.testMode(); } @After diff --git a/src/test/java/org/qora/test/common/AccountUtils.java b/src/test/java/org/qora/test/common/AccountUtils.java index bd5fcfe4..3e7a53e3 100644 --- a/src/test/java/org/qora/test/common/AccountUtils.java +++ b/src/test/java/org/qora/test/common/AccountUtils.java @@ -5,11 +5,9 @@ import java.util.HashMap; import java.util.Map; import org.qora.account.PrivateKeyAccount; -import org.qora.crypto.Crypto; import org.qora.data.transaction.BaseTransactionData; -import org.qora.data.transaction.EnableForgingTransactionData; import org.qora.data.transaction.PaymentTransactionData; -import org.qora.data.transaction.ProxyForgingTransactionData; +import org.qora.data.transaction.RewardShareTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.group.Group; import org.qora.repository.DataException; @@ -30,60 +28,35 @@ public class AccountUtils { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, sendingAccount.getPublicKey(), fee, null); TransactionData transactionData = new PaymentTransactionData(baseTransactionData, recipientAccount.getAddress(), amount); - TransactionUtils.signAndForge(repository, transactionData, sendingAccount); + TransactionUtils.signAndMint(repository, transactionData, sendingAccount); } - public static TransactionData createProxyForging(Repository repository, String forger, String recipient, BigDecimal share) throws DataException { - PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger); + public static TransactionData createRewardShare(Repository repository, String minter, String recipient, BigDecimal sharePercent) throws DataException { + PrivateKeyAccount mintingAccount = Common.getTestAccount(repository, minter); PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); - byte[] reference = forgingAccount.getLastReference(); + byte[] reference = mintingAccount.getLastReference(); long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; - byte[] proxyPrivateKey = forgingAccount.getSharedSecret(recipientAccount.getPublicKey()); - PrivateKeyAccount proxyAccount = new PrivateKeyAccount(null, proxyPrivateKey); + byte[] rewardSharePrivateKey = mintingAccount.getSharedSecret(recipientAccount.getPublicKey()); + PrivateKeyAccount rewardShareAccount = new PrivateKeyAccount(null, rewardSharePrivateKey); - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, forgingAccount.getPublicKey(), fee, null); - TransactionData transactionData = new ProxyForgingTransactionData(baseTransactionData, recipientAccount.getAddress(), proxyAccount.getPublicKey(), share); + BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, mintingAccount.getPublicKey(), fee, null); + TransactionData transactionData = new RewardShareTransactionData(baseTransactionData, recipientAccount.getAddress(), rewardShareAccount.getPublicKey(), sharePercent); return transactionData; } - public static byte[] proxyForging(Repository repository, String forger, String recipient, BigDecimal share) throws DataException { - TransactionData transactionData = createProxyForging(repository, forger, recipient, share); + public static byte[] rewardShare(Repository repository, String minter, String recipient, BigDecimal sharePercent) throws DataException { + TransactionData transactionData = createRewardShare(repository, minter, recipient, sharePercent); - PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger); - TransactionUtils.signAndForge(repository, transactionData, forgingAccount); + PrivateKeyAccount rewardShareAccount = Common.getTestAccount(repository, minter); + TransactionUtils.signAndMint(repository, transactionData, rewardShareAccount); PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); - byte[] proxyPrivateKey = forgingAccount.getSharedSecret(recipientAccount.getPublicKey()); + byte[] rewardSharePrivateKey = rewardShareAccount.getSharedSecret(recipientAccount.getPublicKey()); - return proxyPrivateKey; - } - - public static TransactionData createEnableForging(Repository repository, String forger, byte[] recipientPublicKey) throws DataException { - PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger); - - byte[] reference = forgingAccount.getLastReference(); - long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1; - - BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, forgingAccount.getPublicKey(), fee, null); - return new EnableForgingTransactionData(baseTransactionData, Crypto.toAddress(recipientPublicKey)); - } - - public static TransactionData createEnableForging(Repository repository, String forger, String recipient) throws DataException { - PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); - - return createEnableForging(repository, forger, recipientAccount.getPublicKey()); - } - - public static TransactionData enableForging(Repository repository, String forger, String recipient) throws DataException { - TransactionData transactionData = createEnableForging(repository, forger, recipient); - - PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger); - TransactionUtils.signAndForge(repository, transactionData, forgingAccount); - - return transactionData; + return rewardSharePrivateKey; } public static Map> getBalances(Repository repository, long... assetIds) throws DataException { diff --git a/src/test/java/org/qora/test/common/AssetUtils.java b/src/test/java/org/qora/test/common/AssetUtils.java index fc40afa9..18488e67 100644 --- a/src/test/java/org/qora/test/common/AssetUtils.java +++ b/src/test/java/org/qora/test/common/AssetUtils.java @@ -41,7 +41,7 @@ public class AssetUtils { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, AssetUtils.txGroupId, reference, account.getPublicKey(), AssetUtils.fee, null); TransactionData transactionData = new IssueAssetTransactionData(baseTransactionData, account.getAddress(), assetName, "desc", quantity, isDivisible, "{}", false); - TransactionUtils.signAndForge(repository, transactionData, account); + TransactionUtils.signAndMint(repository, transactionData, account); return repository.getAssetRepository().fromAssetName(assetName).getAssetId(); } @@ -56,7 +56,7 @@ public class AssetUtils { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, AssetUtils.txGroupId, reference, fromAccount.getPublicKey(), AssetUtils.fee, null); TransactionData transactionData = new TransferAssetTransactionData(baseTransactionData, toAccount.getAddress(), amount, assetId); - TransactionUtils.signAndForge(repository, transactionData, fromAccount); + TransactionUtils.signAndMint(repository, transactionData, fromAccount); } public static byte[] createOrder(Repository repository, String accountName, long haveAssetId, long wantAssetId, BigDecimal amount, BigDecimal price) throws DataException { @@ -68,7 +68,7 @@ public class AssetUtils { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, AssetUtils.txGroupId, reference, account.getPublicKey(), AssetUtils.fee, null); TransactionData transactionData = new CreateAssetOrderTransactionData(baseTransactionData, haveAssetId, wantAssetId, amount, price); - TransactionUtils.signAndForge(repository, transactionData, account); + TransactionUtils.signAndMint(repository, transactionData, account); return repository.getAssetRepository().getAccountsOrders(account.getPublicKey(), null, null, null, null, true).get(0).getOrderId(); } @@ -89,7 +89,7 @@ public class AssetUtils { PrivateKeyAccount account = Common.getTestAccount(repository, accountName); Transaction transaction = buildCancelOrder(repository, accountName, orderId); - TransactionUtils.signAndForge(repository, transaction.getTransactionData(), account); + TransactionUtils.signAndMint(repository, transaction.getTransactionData(), account); } public static void genericTradeTest(long haveAssetId, long wantAssetId, diff --git a/src/test/java/org/qora/test/common/Common.java b/src/test/java/org/qora/test/common/Common.java index bbb27be5..123d3e64 100644 --- a/src/test/java/org/qora/test/common/Common.java +++ b/src/test/java/org/qora/test/common/Common.java @@ -72,18 +72,21 @@ public class Common { private static Map testAccountsByName = new HashMap<>(); static { - testAccountsByName.put("alice", new TestAccount(null, "alice", "A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6")); - testAccountsByName.put("bob", new TestAccount(null, "bob", "AdTd9SUEYSdTW8mgK3Gu72K97bCHGdUwi2VvLNjUohot")); - testAccountsByName.put("chloe", new TestAccount(null, "chloe", "HqVngdE1AmEyDpfwTZqUdFHB13o4bCmpoTNAKEqki66K")); - testAccountsByName.put("dilbert", new TestAccount(null, "dilbert", "Gakhh6Ln4vtBFM88nE9JmDaLBDtUBg51aVFpWfSkyVw5")); + testAccountsByName.put("alice", new TestAccount(null, "alice", "A9MNsATgQgruBUjxy2rjWY36Yf19uRioKZbiLFT2P7c6", false)); + testAccountsByName.put("bob", new TestAccount(null, "bob", "AdTd9SUEYSdTW8mgK3Gu72K97bCHGdUwi2VvLNjUohot", false)); + testAccountsByName.put("chloe", new TestAccount(null, "chloe", "HqVngdE1AmEyDpfwTZqUdFHB13o4bCmpoTNAKEqki66K", false)); + testAccountsByName.put("dilbert", new TestAccount(null, "dilbert", "Gakhh6Ln4vtBFM88nE9JmDaLBDtUBg51aVFpWfSkyVw5", false)); + + // Alice reward-share with herself. Private key is reward-share private key, derived from Alice's private and public keys. + testAccountsByName.put("alice-reward-share", new TestAccount(null, "alice-reward-share", "1CeDCg9TSdBwJNGVTGG7pCKsvsyyoEcaVXYvDT1Xb9f", true)); } public static TestAccount getTestAccount(Repository repository, String name) { - return new TestAccount(repository, name, testAccountsByName.get(name).getPrivateKey()); + return new TestAccount(repository, testAccountsByName.get(name)); } public static List getTestAccounts(Repository repository) { - return testAccountsByName.values().stream().map(account -> new TestAccount(repository, account.accountName, account.getPrivateKey())).collect(Collectors.toList()); + return testAccountsByName.values().stream().map(account -> new TestAccount(repository, account)).collect(Collectors.toList()); } public static void useSettings(String settingsFilename) throws DataException { @@ -116,7 +119,8 @@ public class Common { // Check that each test account can fetch their last reference for (TestAccount testAccount : getTestAccounts(repository)) - assertNotNull(String.format("Test account %s / %s should have last reference", testAccount.accountName, testAccount.getAddress()), testAccount.getLastReference()); + if (!testAccount.isRewardShare) + assertNotNull(String.format("Test account %s / %s should have last reference", testAccount.accountName, testAccount.getAddress()), testAccount.getLastReference()); } } diff --git a/src/test/java/org/qora/test/common/GroupUtils.java b/src/test/java/org/qora/test/common/GroupUtils.java index 9fff9cd0..774e2be8 100644 --- a/src/test/java/org/qora/test/common/GroupUtils.java +++ b/src/test/java/org/qora/test/common/GroupUtils.java @@ -30,7 +30,7 @@ public class GroupUtils { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, account.getPublicKey(), GroupUtils.fee, null); TransactionData transactionData = new CreateGroupTransactionData(baseTransactionData, account.getAddress(), groupName, groupDescription, isOpen, approvalThreshold, minimumBlockDelay, maximumBlockDelay); - TransactionUtils.signAndForge(repository, transactionData, account); + TransactionUtils.signAndMint(repository, transactionData, account); return repository.getGroupRepository().fromGroupName(groupName).getGroupId(); } @@ -44,7 +44,7 @@ public class GroupUtils { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, account.getPublicKey(), GroupUtils.fee, null); TransactionData transactionData = new JoinGroupTransactionData(baseTransactionData, groupId); - TransactionUtils.signAndForge(repository, transactionData, account); + TransactionUtils.signAndMint(repository, transactionData, account); } public static void approveTransaction(Repository repository, String accountName, byte[] pendingSignature, boolean decision) throws DataException { @@ -56,7 +56,7 @@ public class GroupUtils { BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, account.getPublicKey(), GroupUtils.fee, null); TransactionData transactionData = new GroupApprovalTransactionData(baseTransactionData, pendingSignature, decision); - TransactionUtils.signAndForge(repository, transactionData, account); + TransactionUtils.signAndMint(repository, transactionData, account); } public static ApprovalStatus getApprovalStatus(Repository repository, byte[] signature) throws DataException { diff --git a/src/test/java/org/qora/test/common/TestAccount.java b/src/test/java/org/qora/test/common/TestAccount.java index 26a9d570..bd95267d 100644 --- a/src/test/java/org/qora/test/common/TestAccount.java +++ b/src/test/java/org/qora/test/common/TestAccount.java @@ -7,15 +7,17 @@ import org.qora.utils.Base58; public class TestAccount extends PrivateKeyAccount { public final String accountName; + public final boolean isRewardShare; - public TestAccount(Repository repository, String accountName, byte[] privateKey) { - super(repository, privateKey); + public TestAccount(Repository repository, String accountName, String privateKey, boolean isRewardShare) { + super(repository, Base58.decode(privateKey)); this.accountName = accountName; + this.isRewardShare = isRewardShare; } - public TestAccount(Repository repository, String accountName, String privateKey) { - this(repository, accountName, Base58.decode(privateKey)); + public TestAccount(Repository repository, TestAccount testAccount) { + this(repository, testAccount.accountName, Base58.encode(testAccount.getPrivateKey()), testAccount.isRewardShare); } } diff --git a/src/test/java/org/qora/test/common/TransactionUtils.java b/src/test/java/org/qora/test/common/TransactionUtils.java index 79119727..8baf4681 100644 --- a/src/test/java/org/qora/test/common/TransactionUtils.java +++ b/src/test/java/org/qora/test/common/TransactionUtils.java @@ -7,7 +7,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.qora.account.PrivateKeyAccount; -import org.qora.block.BlockGenerator; +import org.qora.block.BlockMinter; import org.qora.data.transaction.TransactionData; import org.qora.repository.DataException; import org.qora.repository.Repository; @@ -37,12 +37,12 @@ public class TransactionUtils { } /** Signs transaction using given account and forges a new block, using "alice" account. */ - public static void signAndForge(Repository repository, TransactionData transactionData, PrivateKeyAccount signingAccount) throws DataException { + public static void signAndMint(Repository repository, TransactionData transactionData, PrivateKeyAccount signingAccount) throws DataException { signAsUnconfirmed(repository, transactionData, signingAccount); // Generate block - PrivateKeyAccount generatorAccount = Common.getTestAccount(repository, "alice"); - BlockGenerator.generateTestingBlock(repository, generatorAccount); + PrivateKeyAccount minterAccount = Common.getTestAccount(repository, "alice-reward-share"); + BlockMinter.mintTestingBlock(repository, minterAccount); } public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, TransactionType txType, boolean wantValid) throws DataException { diff --git a/src/test/java/org/qora/test/common/transaction/EnableForgingTestTransaction.java b/src/test/java/org/qora/test/common/transaction/EnableForgingTestTransaction.java deleted file mode 100644 index 9f144688..00000000 --- a/src/test/java/org/qora/test/common/transaction/EnableForgingTestTransaction.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.qora.test.common.transaction; - -import org.qora.account.PrivateKeyAccount; -import org.qora.data.transaction.EnableForgingTransactionData; -import org.qora.data.transaction.TransactionData; -import org.qora.repository.DataException; -import org.qora.repository.Repository; - -public class EnableForgingTestTransaction extends TestTransaction { - - public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { - String target = account.getAddress(); - - return new EnableForgingTransactionData(generateBase(account), target); - } - -} diff --git a/src/test/java/org/qora/test/common/transaction/ProxyForgingTestTransaction.java b/src/test/java/org/qora/test/common/transaction/RewardShareTestTransaction.java similarity index 53% rename from src/test/java/org/qora/test/common/transaction/ProxyForgingTestTransaction.java rename to src/test/java/org/qora/test/common/transaction/RewardShareTestTransaction.java index e2ee3416..9c48df09 100644 --- a/src/test/java/org/qora/test/common/transaction/ProxyForgingTestTransaction.java +++ b/src/test/java/org/qora/test/common/transaction/RewardShareTestTransaction.java @@ -3,19 +3,19 @@ package org.qora.test.common.transaction; import java.math.BigDecimal; import org.qora.account.PrivateKeyAccount; -import org.qora.data.transaction.ProxyForgingTransactionData; +import org.qora.data.transaction.RewardShareTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.repository.DataException; import org.qora.repository.Repository; -public class ProxyForgingTestTransaction extends TestTransaction { +public class RewardShareTestTransaction extends TestTransaction { public static TransactionData randomTransaction(Repository repository, PrivateKeyAccount account, boolean wantValid) throws DataException { String recipient = account.getAddress(); - byte[] proxyPublicKey = account.getProxyPrivateKey(account.getPublicKey()); - BigDecimal share = BigDecimal.valueOf(50); + byte[] rewardSharePublicKey = account.getRewardSharePrivateKey(account.getPublicKey()); + BigDecimal sharePercent = BigDecimal.valueOf(50); - return new ProxyForgingTransactionData(generateBase(account), recipient, proxyPublicKey, share); + return new RewardShareTransactionData(generateBase(account), recipient, rewardSharePublicKey, sharePercent); } } diff --git a/src/test/java/org/qora/test/forging/ProxyForgingTests.java b/src/test/java/org/qora/test/forging/ProxyForgingTests.java deleted file mode 100644 index 631caf63..00000000 --- a/src/test/java/org/qora/test/forging/ProxyForgingTests.java +++ /dev/null @@ -1,127 +0,0 @@ -package org.qora.test.forging; - -import static org.junit.Assert.*; - -import java.math.BigDecimal; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.qora.account.PrivateKeyAccount; -import org.qora.data.account.ProxyForgerData; -import org.qora.data.transaction.TransactionData; -import org.qora.repository.DataException; -import org.qora.repository.Repository; -import org.qora.repository.RepositoryManager; -import org.qora.test.common.AccountUtils; -import org.qora.test.common.BlockUtils; -import org.qora.test.common.Common; -import org.qora.transaction.Transaction; -import org.qora.transaction.Transaction.ValidationResult; -import org.qora.utils.Base58; - -public class ProxyForgingTests extends Common { - - @Before - public void beforeTest() throws DataException { - Common.useDefaultSettings(); - } - - @After - public void afterTest() throws DataException { - Common.orphanCheck(); - } - - @Test - public void testCreateRelationship() throws DataException { - final BigDecimal share = new BigDecimal("12.8"); - - try (final Repository repository = RepositoryManager.getRepository()) { - PrivateKeyAccount aliceAccount = Common.getTestAccount(repository, "alice"); - PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob"); - - // Create relationship - byte[] proxyPrivateKey = AccountUtils.proxyForging(repository, "alice", "bob", share); - PrivateKeyAccount proxyAccount = new PrivateKeyAccount(repository, proxyPrivateKey); - - // Confirm relationship info set correctly - - // Fetch using proxy public key - ProxyForgerData proxyForgerData = repository.getAccountRepository().getProxyForgeData(proxyAccount.getPublicKey()); - assertEquals("Incorrect generator public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(proxyForgerData.getForgerPublicKey())); - assertEquals("Incorrect recipient", bobAccount.getAddress(), proxyForgerData.getRecipient()); - assertEqualBigDecimals("Incorrect reward share", share, proxyForgerData.getShare()); - - // Fetch using generator public key and recipient address combination - proxyForgerData = repository.getAccountRepository().getProxyForgeData(aliceAccount.getPublicKey(), bobAccount.getAddress()); - assertEquals("Incorrect generator public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(proxyForgerData.getForgerPublicKey())); - assertEquals("Incorrect recipient", bobAccount.getAddress(), proxyForgerData.getRecipient()); - assertEqualBigDecimals("Incorrect reward share", share, proxyForgerData.getShare()); - - // Delete relationship - byte[] newProxyPrivateKey = AccountUtils.proxyForging(repository, "alice", "bob", BigDecimal.ZERO); - PrivateKeyAccount newProxyAccount = new PrivateKeyAccount(repository, newProxyPrivateKey); - - // Confirm proxy keys match - assertEquals("Proxy private keys differ", Base58.encode(proxyPrivateKey), Base58.encode(newProxyPrivateKey)); - assertEquals("Proxy public keys differ", Base58.encode(proxyAccount.getPublicKey()), Base58.encode(newProxyAccount.getPublicKey())); - - // Confirm relationship no longer exists in repository - - // Fetch using proxy public key - ProxyForgerData newProxyForgerData = repository.getAccountRepository().getProxyForgeData(proxyAccount.getPublicKey()); - assertNull("Proxy relationship data shouldn't exist", newProxyForgerData); - - // Fetch using generator public key and recipient address combination - newProxyForgerData = repository.getAccountRepository().getProxyForgeData(aliceAccount.getPublicKey(), bobAccount.getAddress()); - assertNull("Proxy relationship data shouldn't exist", newProxyForgerData); - - // Orphan last block to restore prior proxy relationship - BlockUtils.orphanLastBlock(repository); - - // Confirm proxy relationship restored correctly - - // Fetch using proxy public key - newProxyForgerData = repository.getAccountRepository().getProxyForgeData(proxyAccount.getPublicKey()); - assertNotNull("Proxy relationship data should have been restored", newProxyForgerData); - assertEquals("Incorrect generator public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(newProxyForgerData.getForgerPublicKey())); - assertEquals("Incorrect recipient", bobAccount.getAddress(), newProxyForgerData.getRecipient()); - assertEqualBigDecimals("Incorrect reward share", share, newProxyForgerData.getShare()); - - // Fetch using generator public key and recipient address combination - newProxyForgerData = repository.getAccountRepository().getProxyForgeData(aliceAccount.getPublicKey(), bobAccount.getAddress()); - assertNotNull("Proxy relationship data should have been restored", newProxyForgerData); - assertEquals("Incorrect generator public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(newProxyForgerData.getForgerPublicKey())); - assertEquals("Incorrect recipient", bobAccount.getAddress(), newProxyForgerData.getRecipient()); - assertEqualBigDecimals("Incorrect reward share", share, newProxyForgerData.getShare()); - - // Orphan another block to remove initial proxy relationship - BlockUtils.orphanLastBlock(repository); - - // Confirm proxy relationship no longer exists - - // Fetch using proxy public key - newProxyForgerData = repository.getAccountRepository().getProxyForgeData(proxyAccount.getPublicKey()); - assertNull("Proxy relationship data shouldn't exist", newProxyForgerData); - - // Fetch using generator public key and recipient address combination - newProxyForgerData = repository.getAccountRepository().getProxyForgeData(aliceAccount.getPublicKey(), bobAccount.getAddress()); - assertNull("Proxy relationship data shouldn't exist", newProxyForgerData); - } - } - - @Test - public void testZeroInitialShareInvalid() throws DataException { - try (final Repository repository = RepositoryManager.getRepository()) { - // Create invalid PROXY_FORGING transaction with initial 0% reward share - TransactionData transactionData = AccountUtils.createProxyForging(repository, "alice", "bob", BigDecimal.ZERO); - - // Confirm transaction is invalid - Transaction transaction = Transaction.fromData(repository, transactionData); - - ValidationResult validationResult = transaction.isValidUnconfirmed(); - assertEquals("Initial 0% share should be invalid", ValidationResult.INVALID_FORGE_SHARE, validationResult); - } - } - -} diff --git a/src/test/java/org/qora/test/forging/RewardShareTests.java b/src/test/java/org/qora/test/forging/RewardShareTests.java new file mode 100644 index 00000000..300b702e --- /dev/null +++ b/src/test/java/org/qora/test/forging/RewardShareTests.java @@ -0,0 +1,127 @@ +package org.qora.test.forging; + +import static org.junit.Assert.*; + +import java.math.BigDecimal; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.qora.account.PrivateKeyAccount; +import org.qora.data.account.RewardShareData; +import org.qora.data.transaction.TransactionData; +import org.qora.repository.DataException; +import org.qora.repository.Repository; +import org.qora.repository.RepositoryManager; +import org.qora.test.common.AccountUtils; +import org.qora.test.common.BlockUtils; +import org.qora.test.common.Common; +import org.qora.transaction.Transaction; +import org.qora.transaction.Transaction.ValidationResult; +import org.qora.utils.Base58; + +public class RewardShareTests extends Common { + + @Before + public void beforeTest() throws DataException { + Common.useDefaultSettings(); + } + + @After + public void afterTest() throws DataException { + Common.orphanCheck(); + } + + @Test + public void testCreateRewardShare() throws DataException { + final BigDecimal sharePercent = new BigDecimal("12.8"); + + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount aliceAccount = Common.getTestAccount(repository, "alice"); + PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob"); + + // Create reward-share + byte[] rewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", sharePercent); + PrivateKeyAccount rewardShareAccount = new PrivateKeyAccount(repository, rewardSharePrivateKey); + + // Confirm reward-share info set correctly + + // Fetch using reward-share public key + RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(rewardShareAccount.getPublicKey()); + assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(rewardShareData.getMinterPublicKey())); + assertEquals("Incorrect recipient", bobAccount.getAddress(), rewardShareData.getRecipient()); + assertEqualBigDecimals("Incorrect share percentage", sharePercent, rewardShareData.getSharePercent()); + + // Fetch using minter public key and recipient address combination + rewardShareData = repository.getAccountRepository().getRewardShare(aliceAccount.getPublicKey(), bobAccount.getAddress()); + assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(rewardShareData.getMinterPublicKey())); + assertEquals("Incorrect recipient", bobAccount.getAddress(), rewardShareData.getRecipient()); + assertEqualBigDecimals("Incorrect share percentage", sharePercent, rewardShareData.getSharePercent()); + + // Delete reward-share + byte[] newRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", BigDecimal.ZERO); + PrivateKeyAccount newRewardShareAccount = new PrivateKeyAccount(repository, newRewardSharePrivateKey); + + // Confirm reward-share keys match + assertEquals("Reward-share private keys differ", Base58.encode(rewardSharePrivateKey), Base58.encode(newRewardSharePrivateKey)); + assertEquals("Reward-share public keys differ", Base58.encode(rewardShareAccount.getPublicKey()), Base58.encode(newRewardShareAccount.getPublicKey())); + + // Confirm reward-share no longer exists in repository + + // Fetch using reward-share public key + RewardShareData newRewardShareData = repository.getAccountRepository().getRewardShare(rewardShareAccount.getPublicKey()); + assertNull("Reward-share shouldn't exist", newRewardShareData); + + // Fetch using minter public key and recipient address combination + newRewardShareData = repository.getAccountRepository().getRewardShare(aliceAccount.getPublicKey(), bobAccount.getAddress()); + assertNull("Reward-share shouldn't exist", newRewardShareData); + + // Orphan last block to restore prior reward-share + BlockUtils.orphanLastBlock(repository); + + // Confirm reward-share restored correctly + + // Fetch using reward-share public key + newRewardShareData = repository.getAccountRepository().getRewardShare(rewardShareAccount.getPublicKey()); + assertNotNull("Reward-share should have been restored", newRewardShareData); + assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(newRewardShareData.getMinterPublicKey())); + assertEquals("Incorrect recipient", bobAccount.getAddress(), newRewardShareData.getRecipient()); + assertEqualBigDecimals("Incorrect share percentage", sharePercent, newRewardShareData.getSharePercent()); + + // Fetch using minter public key and recipient address combination + newRewardShareData = repository.getAccountRepository().getRewardShare(aliceAccount.getPublicKey(), bobAccount.getAddress()); + assertNotNull("Reward-share should have been restored", newRewardShareData); + assertEquals("Incorrect minter public key", Base58.encode(aliceAccount.getPublicKey()), Base58.encode(newRewardShareData.getMinterPublicKey())); + assertEquals("Incorrect recipient", bobAccount.getAddress(), newRewardShareData.getRecipient()); + assertEqualBigDecimals("Incorrect share percentage", sharePercent, newRewardShareData.getSharePercent()); + + // Orphan another block to remove initial reward-share + BlockUtils.orphanLastBlock(repository); + + // Confirm reward-share no longer exists + + // Fetch using reward-share public key + newRewardShareData = repository.getAccountRepository().getRewardShare(rewardShareAccount.getPublicKey()); + assertNull("Reward-share shouldn't exist", newRewardShareData); + + // Fetch using minter public key and recipient address combination + newRewardShareData = repository.getAccountRepository().getRewardShare(aliceAccount.getPublicKey(), bobAccount.getAddress()); + assertNull("Reward-share shouldn't exist", newRewardShareData); + } + } + + @Test + public void testZeroInitialShareInvalid() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + // Create invalid REWARD_SHARE transaction with initial 0% reward share + TransactionData transactionData = AccountUtils.createRewardShare(repository, "alice", "bob", BigDecimal.ZERO); + + // Confirm transaction is invalid + Transaction transaction = Transaction.fromData(repository, transactionData); + + ValidationResult validationResult = transaction.isValidUnconfirmed(); + assertEquals("Initial 0% share should be invalid", ValidationResult.INVALID_REWARD_SHARE_PERCENT, validationResult); + } + } + +} diff --git a/src/test/java/org/qora/test/forging/RewardTests.java b/src/test/java/org/qora/test/forging/RewardTests.java index a78fc750..3df0db9d 100644 --- a/src/test/java/org/qora/test/forging/RewardTests.java +++ b/src/test/java/org/qora/test/forging/RewardTests.java @@ -12,7 +12,7 @@ import org.qora.account.PrivateKeyAccount; import org.qora.asset.Asset; import org.qora.block.BlockChain; import org.qora.block.BlockChain.RewardByHeight; -import org.qora.block.BlockGenerator; +import org.qora.block.BlockMinter; import org.qora.repository.DataException; import org.qora.repository.Repository; import org.qora.repository.RepositoryManager; @@ -41,7 +41,7 @@ public class RewardTests extends Common { BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); - BlockGenerator.generateTestingBlock(repository, forgingAccount); + BlockMinter.mintTestingBlock(repository, forgingAccount); BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORT).add(blockReward); AccountUtils.assertBalance(repository, "alice", Asset.QORT, expectedBalance); @@ -68,7 +68,7 @@ public class RewardTests extends Common { rewardInfo = rewards.get(rewardIndex); } - BlockGenerator.generateTestingBlock(repository, forgingAccount); + BlockMinter.mintTestingBlock(repository, forgingAccount); expectedBalance = expectedBalance.add(rewardInfo.reward); } @@ -81,12 +81,12 @@ public class RewardTests extends Common { final BigDecimal share = new BigDecimal("12.8"); try (final Repository repository = RepositoryManager.getRepository()) { - byte[] proxyPrivateKey = AccountUtils.proxyForging(repository, "alice", "bob", share); + byte[] proxyPrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", share); PrivateKeyAccount proxyAccount = new PrivateKeyAccount(repository, proxyPrivateKey); Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT); BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); - BlockGenerator.generateTestingBlock(repository, proxyAccount); + BlockMinter.mintTestingBlock(repository, proxyAccount); // We're expecting reward * 12.8% to Bob, the rest to Alice diff --git a/src/test/java/org/qora/test/group/GroupApprovalTests.java b/src/test/java/org/qora/test/group/GroupApprovalTests.java index 0d0ca29a..9d60e4bc 100644 --- a/src/test/java/org/qora/test/group/GroupApprovalTests.java +++ b/src/test/java/org/qora/test/group/GroupApprovalTests.java @@ -5,7 +5,7 @@ import org.junit.Before; import org.junit.Test; import org.qora.account.PrivateKeyAccount; import org.qora.asset.Asset; -import org.qora.block.BlockGenerator; +import org.qora.block.BlockMinter; import org.qora.data.transaction.BaseTransactionData; import org.qora.data.transaction.IssueAssetTransactionData; import org.qora.data.transaction.PaymentTransactionData; @@ -69,7 +69,7 @@ public class GroupApprovalTests extends Common { int groupId = GroupUtils.createGroup(repository, "alice", "test", true, ApprovalThreshold.ONE, minBlockDelay, maxBlockDelay); Transaction transaction = buildIssueAssetTransaction(repository, "alice", groupId); - TransactionUtils.signAndForge(repository, transaction.getTransactionData(), aliceAccount); + TransactionUtils.signAndMint(repository, transaction.getTransactionData(), aliceAccount); // Confirm transaction doesn't need approval ApprovalStatus approvalStatus = GroupUtils.getApprovalStatus(repository, transaction.getTransactionData().getSignature()); @@ -95,7 +95,7 @@ public class GroupApprovalTests extends Common { BigDecimal blockReward = BlockUtils.getNextBlockReward(repository); Transaction bobAssetTransaction = buildIssueAssetTransaction(repository, "bob", groupId); - TransactionUtils.signAndForge(repository, bobAssetTransaction.getTransactionData(), bobAccount); + TransactionUtils.signAndMint(repository, bobAssetTransaction.getTransactionData(), bobAccount); // Confirm transaction needs approval, and hasn't been approved ApprovalStatus approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -115,7 +115,7 @@ public class GroupApprovalTests extends Common { // Have Bob do a non-approval transaction to change his last-reference Transaction bobPaymentTransaction = buildPaymentTransaction(repository, "bob", "chloe", amount, Group.NO_GROUP); - TransactionUtils.signAndForge(repository, bobPaymentTransaction.getTransactionData(), bobAccount); + TransactionUtils.signAndMint(repository, bobPaymentTransaction.getTransactionData(), bobAccount); byte[] bobPostPaymentReference = bobAccount.getLastReference(); assertFalse("reference should have changed", Arrays.equals(bobPostAssetReference, bobPostPaymentReference)); @@ -125,7 +125,7 @@ public class GroupApprovalTests extends Common { // Now forge a few blocks so transaction is approved for (int blockCount = 0; blockCount < minBlockDelay; ++blockCount) - BlockGenerator.generateTestingBlock(repository, aliceAccount); + BlockMinter.mintTestingBlock(repository, aliceAccount); // Confirm transaction now approved approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -184,7 +184,7 @@ public class GroupApprovalTests extends Common { // Bob's issue-asset transaction needs group-approval Transaction bobAssetTransaction = buildIssueAssetTransaction(repository, "bob", groupId); - TransactionUtils.signAndForge(repository, bobAssetTransaction.getTransactionData(), bobAccount); + TransactionUtils.signAndMint(repository, bobAssetTransaction.getTransactionData(), bobAccount); // Confirm transaction needs approval, and hasn't been approved ApprovalStatus approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -199,7 +199,7 @@ public class GroupApprovalTests extends Common { // Now forge a few blocks so transaction is approved for (int blockCount = 0; blockCount < minBlockDelay; ++blockCount) - BlockGenerator.generateTestingBlock(repository, aliceAccount); + BlockMinter.mintTestingBlock(repository, aliceAccount); // Confirm transaction now approved approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -246,7 +246,7 @@ public class GroupApprovalTests extends Common { // Bob's issue-asset transaction needs group-approval Transaction bobAssetTransaction = buildIssueAssetTransaction(repository, "bob", groupId); - TransactionUtils.signAndForge(repository, bobAssetTransaction.getTransactionData(), bobAccount); + TransactionUtils.signAndMint(repository, bobAssetTransaction.getTransactionData(), bobAccount); // Confirm transaction needs approval, and hasn't been approved ApprovalStatus approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -261,7 +261,7 @@ public class GroupApprovalTests extends Common { // Now forge a few blocks so transaction is approved for (int blockCount = 0; blockCount < minBlockDelay; ++blockCount) - BlockGenerator.generateTestingBlock(repository, aliceAccount); + BlockMinter.mintTestingBlock(repository, aliceAccount); // Confirm transaction now rejected approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -308,7 +308,7 @@ public class GroupApprovalTests extends Common { // Bob's issue-asset transaction needs group-approval Transaction bobAssetTransaction = buildIssueAssetTransaction(repository, "bob", groupId); - TransactionUtils.signAndForge(repository, bobAssetTransaction.getTransactionData(), bobAccount); + TransactionUtils.signAndMint(repository, bobAssetTransaction.getTransactionData(), bobAccount); // Confirm transaction needs approval, and hasn't been approved ApprovalStatus approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -320,7 +320,7 @@ public class GroupApprovalTests extends Common { // Now forge a few blocks so group-approval for transaction expires for (int blockCount = 0; blockCount <= maxBlockDelay; ++blockCount) - BlockGenerator.generateTestingBlock(repository, aliceAccount); + BlockMinter.mintTestingBlock(repository, aliceAccount); // Confirm transaction now expired approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -356,7 +356,7 @@ public class GroupApprovalTests extends Common { // Bob's issue-asset transaction needs group-approval Transaction bobAssetTransaction = buildIssueAssetTransaction(repository, "bob", groupId); - TransactionUtils.signAndForge(repository, bobAssetTransaction.getTransactionData(), bobAccount); + TransactionUtils.signAndMint(repository, bobAssetTransaction.getTransactionData(), bobAccount); // Confirm transaction needs approval, and hasn't been approved ApprovalStatus approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); @@ -372,7 +372,7 @@ public class GroupApprovalTests extends Common { // But wait! Alice issues an asset with the same name before Bob's asset is issued! // This transaction will be auto-approved as Alice is the group owner (and admin) Transaction aliceAssetTransaction = buildIssueAssetTransaction(repository, "alice", groupId); - TransactionUtils.signAndForge(repository, aliceAssetTransaction.getTransactionData(), aliceAccount); + TransactionUtils.signAndMint(repository, aliceAssetTransaction.getTransactionData(), aliceAccount); // Confirm Alice's transaction auto-approved approvalStatus = GroupUtils.getApprovalStatus(repository, aliceAssetTransaction.getTransactionData().getSignature()); @@ -380,7 +380,7 @@ public class GroupApprovalTests extends Common { // Now forge a few blocks so transaction is approved for (int blockCount = 0; blockCount < minBlockDelay; ++blockCount) - BlockGenerator.generateTestingBlock(repository, aliceAccount); + BlockMinter.mintTestingBlock(repository, aliceAccount); // Confirm Bob's transaction now invalid approvalStatus = GroupUtils.getApprovalStatus(repository, bobAssetTransaction.getTransactionData().getSignature()); diff --git a/src/test/java/org/qora/test/naming/OrphaningTests.java b/src/test/java/org/qora/test/naming/OrphaningTests.java index cef13145..664598ed 100644 --- a/src/test/java/org/qora/test/naming/OrphaningTests.java +++ b/src/test/java/org/qora/test/naming/OrphaningTests.java @@ -9,7 +9,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.qora.account.PrivateKeyAccount; -import org.qora.block.BlockGenerator; +import org.qora.block.BlockMinter; import org.qora.data.naming.NameData; import org.qora.data.transaction.BuyNameTransactionData; import org.qora.data.transaction.RegisterNameTransactionData; @@ -60,7 +60,7 @@ public class OrphaningTests extends Common { public void testRegisterName() throws DataException { // Register-name RegisterNameTransactionData transactionData = new RegisterNameTransactionData(TestTransaction.generateBase(alice), alice.getAddress(), name, "{}"); - TransactionUtils.signAndForge(repository, transactionData, alice); + TransactionUtils.signAndMint(repository, transactionData, alice); String name = transactionData.getName(); @@ -74,7 +74,7 @@ public class OrphaningTests extends Common { assertFalse(repository.getNameRepository().nameExists(name)); // Re-process register-name - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Check name does exist assertTrue(repository.getNameRepository().nameExists(name)); @@ -87,7 +87,7 @@ public class OrphaningTests extends Common { // Sell-name SellNameTransactionData transactionData = new SellNameTransactionData(TestTransaction.generateBase(alice), name, price); - TransactionUtils.signAndForge(repository, transactionData, alice); + TransactionUtils.signAndMint(repository, transactionData, alice); NameData nameData; @@ -105,7 +105,7 @@ public class OrphaningTests extends Common { // Not concerned about price // Re-process sell-name - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Check name is for sale nameData = repository.getNameRepository().fromName(name); @@ -121,10 +121,10 @@ public class OrphaningTests extends Common { assertNull(nameData); // Re-process register-name and sell-name - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Unconfirmed sell-name transaction not included in previous block // as it isn't valid until name exists thanks to register-name transaction. - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Check name does exist assertTrue(repository.getNameRepository().nameExists(name)); @@ -144,7 +144,7 @@ public class OrphaningTests extends Common { // Buy-name BuyNameTransactionData transactionData = new BuyNameTransactionData(TestTransaction.generateBase(bob), name, price, seller); - TransactionUtils.signAndForge(repository, transactionData, bob); + TransactionUtils.signAndMint(repository, transactionData, bob); NameData nameData; @@ -162,7 +162,7 @@ public class OrphaningTests extends Common { assertEqualBigDecimals("price incorrect", price, nameData.getSalePrice()); // Re-process buy-name - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Check name is sold nameData = repository.getNameRepository().fromName(name); @@ -180,10 +180,10 @@ public class OrphaningTests extends Common { assertEquals(alice.getAddress(), nameData.getOwner()); // Re-process sell-name and buy-name - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Unconfirmed buy-name transaction not included in previous block // as it isn't valid until name is for sale thanks to sell-name transaction. - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Check name is sold nameData = repository.getNameRepository().fromName(name); @@ -200,7 +200,7 @@ public class OrphaningTests extends Common { // Sell-name BigDecimal newPrice = new BigDecimal(random.nextInt(1000)).setScale(8); SellNameTransactionData transactionData = new SellNameTransactionData(TestTransaction.generateBase(bob), name, newPrice); - TransactionUtils.signAndForge(repository, transactionData, bob); + TransactionUtils.signAndMint(repository, transactionData, bob); NameData nameData; @@ -218,7 +218,7 @@ public class OrphaningTests extends Common { // Not concerned about price // Re-process sell-name - BlockGenerator.generateTestingBlock(repository, alice); + BlockMinter.mintTestingBlock(repository, alice); // Check name is for sale nameData = repository.getNameRepository().fromName(name); @@ -236,10 +236,10 @@ public class OrphaningTests extends Common { assertEquals(alice.getAddress(), nameData.getOwner()); // Re-process buy-name and sell-name - BlockGenerator.generateTestingBlock(repository, bob); + BlockMinter.mintTestingBlock(repository, bob); // Unconfirmed sell-name transaction not included in previous block // as it isn't valid until name owned by bob thanks to buy-name transaction. - BlockGenerator.generateTestingBlock(repository, bob); + BlockMinter.mintTestingBlock(repository, bob); // Check name does exist assertTrue(repository.getNameRepository().nameExists(name)); diff --git a/src/test/resources/test-chain-old-asset.json b/src/test/resources/test-chain-old-asset.json index 12c4f95e..bc7dab6c 100644 --- a/src/test/resources/test-chain-old-asset.json +++ b/src/test/resources/test-chain-old-asset.json @@ -1,28 +1,32 @@ { "isTestChain": true, - "maxBalance": "10000000000", - "blockDifficultyInterval": 10, - "minBlockTime": 30, - "maxBlockTime": 60, "blockTimestampMargin": 500, + "transactionExpiryPeriod": 86400000, + "maxBlockSize": 2097152, "maxBytesPerUnitFee": 1024, "unitFee": "0.1", "requireGroupForApproval": false, - "genesisInfo": { - "version": 4, - "timestamp": 0, - "generatingBalance": "10000000", - "transactions": [ - { "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" }, - { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 }, - { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, - { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, - { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 } - ] - }, + "minAccountLevelToRewardShare": 5, + "maxRewardSharesPerMintingAccount": 20, + "onlineAccountSignaturesMinLifetime": 3600000, + "onlineAccountSignaturesMaxLifetime": 86400000, + "rewardsByHeight": [ + { "height": 1, "reward": 100 }, + { "height": 11, "reward": 10 }, + { "height": 21, "reward": 1 } + ], + "sharesByLevel": [ + { "levels": [ 1, 2 ], "share": 0.05 }, + { "levels": [ 3, 4 ], "share": 0.10 }, + { "levels": [ 5, 6 ], "share": 0.15 }, + { "levels": [ 7, 8 ], "share": 0.20 }, + { "levels": [ 9, 10 ], "share": 0.25 } + ], + "qoraHoldersShare": 0.20, + "blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ], + "blockTimingsByHeight": [ + { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } + ], "featureTriggers": { "messageHeight": 0, "atHeight": 0, @@ -31,6 +35,23 @@ "arbitraryTimestamp": 0, "powfixTimestamp": 0, "v2Timestamp": 0, - "newAssetPricingTimestamp": 1600000000000 + "newAssetPricingTimestamp": 1600000000000, + "groupApprovalTimestamp": 0 + }, + "genesisInfo": { + "version": 4, + "timestamp": 0, + "transactions": [ + { "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" }, + { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 }, + { "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 } + ] } } diff --git a/src/test/resources/test-chain-v1.json b/src/test/resources/test-chain-v1.json new file mode 100644 index 00000000..13c9cc54 --- /dev/null +++ b/src/test/resources/test-chain-v1.json @@ -0,0 +1,52 @@ +{ + "isTestChain": true, + "blockTimestampMargin": 500, + "transactionExpiryPeriod": 86400000, + "maxBlockSize": 2097152, + "maxBytesPerUnitFee": 1024, + "unitFee": "0.1", + "requireGroupForApproval": false, + "minAccountLevelToRewardShare": 5, + "maxRewardSharesPerMintingAccount": 20, + "onlineAccountSignaturesMinLifetime": 3600000, + "onlineAccountSignaturesMaxLifetime": 86400000, + "rewardsByHeight": [ + { "height": 1, "reward": 100 }, + { "height": 11, "reward": 10 }, + { "height": 21, "reward": 1 } + ], + "sharesByLevel": [ + { "levels": [ 1, 2 ], "share": 0.05 }, + { "levels": [ 3, 4 ], "share": 0.10 }, + { "levels": [ 5, 6 ], "share": 0.15 }, + { "levels": [ 7, 8 ], "share": 0.20 }, + { "levels": [ 9, 10 ], "share": 0.25 } + ], + "qoraHoldersShare": 0.20, + "blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ], + "blockTimingsByHeight": [ + { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } + ], + "featureTriggers": { + "messageHeight": 0, + "atHeight": 0, + "assetsTimestamp": 0, + "votingTimestamp": 0, + "arbitraryTimestamp": 0, + "powfixTimestamp": 0, + "v2Timestamp": 1600000000000, + "newAssetPricingTimestamp": 1600000000000, + "groupApprovalTimestamp": 0 + }, + "genesisInfo": { + "version": 1, + "timestamp": 1400247274336, + "transactions": [ + { "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" }, + { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 } + ] + } +} diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 033446c0..ccc33ed1 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -1,40 +1,31 @@ { "isTestChain": true, - "maxBalance": "10000000000", - "blockDifficultyInterval": 10, - "minBlockTime": 30, - "maxBlockTime": 60, "blockTimestampMargin": 500, + "transactionExpiryPeriod": 86400000, + "maxBlockSize": 2097152, "maxBytesPerUnitFee": 1024, "unitFee": "0.1", "requireGroupForApproval": false, - "maxProxyRelationships": 8, - "genesisInfo": { - "version": 4, - "timestamp": 0, - "generatingBalance": "10000000", - "transactions": [ - { "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" }, - { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 }, - { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, - { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, - { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 }, - { "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, - { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, - { "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 } - ] - }, + "minAccountLevelToRewardShare": 5, + "maxRewardSharesPerMintingAccount": 20, + "onlineAccountSignaturesMinLifetime": 3600000, + "onlineAccountSignaturesMaxLifetime": 86400000, "rewardsByHeight": [ { "height": 1, "reward": 100 }, { "height": 11, "reward": 10 }, { "height": 21, "reward": 1 } ], - "forgingTiers": [ - { "minBlocks": 5, "maxSubAccounts": 5 }, - { "minBlocks": 4, "maxSubAccounts": 3 }, - { "minBlocks": 0, "maxSubAccounts": 0 } + "sharesByLevel": [ + { "levels": [ 1, 2 ], "share": 0.05 }, + { "levels": [ 3, 4 ], "share": 0.10 }, + { "levels": [ 5, 6 ], "share": 0.15 }, + { "levels": [ 7, 8 ], "share": 0.20 }, + { "levels": [ 9, 10 ], "share": 0.25 } + ], + "qoraHoldersShare": 0.20, + "blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ], + "blockTimingsByHeight": [ + { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } ], "featureTriggers": { "messageHeight": 0, @@ -46,5 +37,22 @@ "v2Timestamp": 0, "newAssetPricingTimestamp": 0, "groupApprovalTimestamp": 0 + }, + "genesisInfo": { + "version": 4, + "timestamp": 0, + "transactions": [ + { "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORA", "description": "QORA native coin", "data": "", "quantity": 10000000000, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" }, + { "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000", "fee": 0 }, + { "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000", "fee": 0 }, + { "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ISSUE_ASSET", "owner": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 }, + { "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 }, + { "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 } + ] } } diff --git a/src/test/resources/test-settings-v1.json b/src/test/resources/test-settings-v1.json new file mode 100644 index 00000000..675d0c51 --- /dev/null +++ b/src/test/resources/test-settings-v1.json @@ -0,0 +1,6 @@ +{ + "restrictedApi": false, + "blockchainConfig": "src/test/resources/test-chain-v1.json", + "wipeUnconfirmedOnStart": false, + "minPeers": 0 +}