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 +}