From 93230e9704677d083bce3e49e90c3530f76a6212 Mon Sep 17 00:00:00 2001 From: catbref Date: Wed, 17 Apr 2019 18:11:16 +0100 Subject: [PATCH] Add API call to list blocks with given generator. +more tests +pad genesis public key --- .../java/org/qora/account/GenesisAccount.java | 2 +- .../org/qora/api/resource/BlocksResource.java | 44 +++++++++++++++++++ .../ProxyForgingTransactionData.java | 4 ++ .../org/qora/repository/BlockRepository.java | 5 +++ .../hsqldb/HSQLDBBlockRepository.java | 24 ++++++++++ .../org/qora/test/common/AccountUtils.java | 37 ++++++++++++++++ .../org/qora/test/forging/RewardTests.java | 29 +++++++++++- 7 files changed, 143 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/qora/account/GenesisAccount.java b/src/main/java/org/qora/account/GenesisAccount.java index 17f250f2..d7d83936 100644 --- a/src/main/java/org/qora/account/GenesisAccount.java +++ b/src/main/java/org/qora/account/GenesisAccount.java @@ -4,7 +4,7 @@ import org.qora.repository.Repository; public final class GenesisAccount extends PublicKeyAccount { - public static final byte[] PUBLIC_KEY = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1 }; + public static final byte[] PUBLIC_KEY = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; public GenesisAccount(Repository repository) { super(repository, PUBLIC_KEY); diff --git a/src/main/java/org/qora/api/resource/BlocksResource.java b/src/main/java/org/qora/api/resource/BlocksResource.java index 415f94ee..62b8e445 100644 --- a/src/main/java/org/qora/api/resource/BlocksResource.java +++ b/src/main/java/org/qora/api/resource/BlocksResource.java @@ -29,6 +29,8 @@ import org.qora.api.ApiErrors; import org.qora.api.ApiException; import org.qora.api.ApiExceptionFactory; import org.qora.block.Block; +import org.qora.crypto.Crypto; +import org.qora.data.account.AccountData; import org.qora.data.block.BlockData; import org.qora.data.transaction.EnableForgingTransactionData; import org.qora.data.transaction.TransactionData; @@ -526,6 +528,48 @@ public class BlocksResource { } } + @GET + @Path("/forger/{address}") + @Operation( + summary = "Fetch blocks forged by address", + responses = { + @ApiResponse( + description = "blocks", + content = @Content( + array = @ArraySchema( + schema = @Schema( + implementation = BlockData.class + ) + ) + ) + ) + } + ) + @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.PUBLIC_KEY_NOT_FOUND, ApiError.REPOSITORY_ISSUE}) + public List getBlocksByForger(@PathParam("address") String address, @Parameter( + ref = "limit" + ) @QueryParam("limit") Integer limit, @Parameter( + ref = "offset" + ) @QueryParam("offset") Integer offset, @Parameter( + ref = "reverse" + ) @QueryParam("reverse") Boolean reverse) { + if (!Crypto.isValidAddress(address)) + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS); + + try (final Repository repository = RepositoryManager.getRepository()) { + // Get public key from address + AccountData accountData = repository.getAccountRepository().getAccount(address); + 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); + } catch (ApiException e) { + throw e; + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + } + @POST @Path("/enableforging") @Operation( diff --git a/src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java b/src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java index 2e645cac..8bb83360 100644 --- a/src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java +++ b/src/main/java/org/qora/data/transaction/ProxyForgingTransactionData.java @@ -47,6 +47,10 @@ public class ProxyForgingTransactionData extends TransactionData { this.previousShare = previousShare; } + public ProxyForgingTransactionData(long timestamp, int groupId, byte[] reference, byte[] forgerPublicKey, String recipient, byte[] proxyPublicKey, BigDecimal share, BigDecimal fee) { + this(timestamp, groupId, reference, forgerPublicKey, recipient, proxyPublicKey, share, fee, null); + } + // Used in deserialization context public ProxyForgingTransactionData(long timestamp, int groupId, byte[] reference, byte[] forgerPublicKey, String recipient, byte[] proxyPublicKey, BigDecimal share, BigDecimal fee, byte[] signature) { this(timestamp, groupId, reference, forgerPublicKey, recipient, proxyPublicKey, share, null, fee, signature); diff --git a/src/main/java/org/qora/repository/BlockRepository.java b/src/main/java/org/qora/repository/BlockRepository.java index 2ca0260f..4d39dc38 100644 --- a/src/main/java/org/qora/repository/BlockRepository.java +++ b/src/main/java/org/qora/repository/BlockRepository.java @@ -101,6 +101,11 @@ public interface BlockRepository { */ public int countForgedBlocks(byte[] publicKey) throws DataException; + /** + * Returns blocks with passed generator public key. + */ + public List getBlocksWithGenerator(byte[] generatorPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException; + /** * Saves block into repository. * diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java index 74982a17..4f42ec4d 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java @@ -158,6 +158,30 @@ public class HSQLDBBlockRepository implements BlockRepository { } } + @Override + public List getBlocksWithGenerator(byte[] generatorPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException { + String sql = "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE generator = ? ORDER BY height "; + if (reverse != null && reverse) + sql += " DESC"; + + sql += HSQLDBRepository.limitOffsetSql(limit, offset); + + List blockData = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql, generatorPublicKey)) { + if (resultSet == null) + return blockData; + + do { + blockData.add(getBlockFromResultSet(resultSet)); + } while (resultSet.next()); + + return blockData; + } catch (SQLException e) { + throw new DataException("Unable to fetch generator's blocks from repository", e); + } + } + @Override public void save(BlockData blockData) throws DataException { HSQLDBSaver saveHelper = new HSQLDBSaver("Blocks"); diff --git a/src/test/java/org/qora/test/common/AccountUtils.java b/src/test/java/org/qora/test/common/AccountUtils.java index 6045c75e..08f602cd 100644 --- a/src/test/java/org/qora/test/common/AccountUtils.java +++ b/src/test/java/org/qora/test/common/AccountUtils.java @@ -4,11 +4,48 @@ import java.math.BigDecimal; import java.util.HashMap; import java.util.Map; +import org.qora.account.PrivateKeyAccount; +import org.qora.data.transaction.PaymentTransactionData; +import org.qora.data.transaction.ProxyForgingTransactionData; +import org.qora.data.transaction.TransactionData; +import org.qora.group.Group; import org.qora.repository.DataException; import org.qora.repository.Repository; public class AccountUtils { + public static final int txGroupId = Group.NO_GROUP; + public static final BigDecimal fee = BigDecimal.ONE.setScale(8); + + public static void pay(Repository repository, String sender, String recipient, BigDecimal amount) throws DataException { + PrivateKeyAccount sendingAccount = Common.getTestAccount(repository, sender); + PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); + + byte[] reference = sendingAccount.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1000; + + TransactionData transactionData = new PaymentTransactionData(timestamp, txGroupId, reference, sendingAccount.getPublicKey(), recipientAccount.getAddress(), amount, fee); + + TransactionUtils.signAndForge(repository, transactionData, sendingAccount); + } + + public static byte[] proxyForging(Repository repository, String forger, String recipient, BigDecimal share) throws DataException { + PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger); + PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); + + byte[] reference = forgingAccount.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1000; + + byte[] proxyPrivateKey = forgingAccount.getSharedSecret(recipientAccount.getPublicKey()); + PrivateKeyAccount proxyAccount = new PrivateKeyAccount(null, proxyPrivateKey); + + TransactionData transactionData = new ProxyForgingTransactionData(timestamp, txGroupId, reference, forgingAccount.getPublicKey(), recipientAccount.getAddress(), proxyAccount.getPublicKey(), share, fee); + + TransactionUtils.signAndForge(repository, transactionData, forgingAccount); + + return proxyPrivateKey; + } + public static Map> getBalances(Repository repository, long... assetIds) throws DataException { Map> balances = new HashMap<>(); diff --git a/src/test/java/org/qora/test/forging/RewardTests.java b/src/test/java/org/qora/test/forging/RewardTests.java index 3763d24b..aabe710c 100644 --- a/src/test/java/org/qora/test/forging/RewardTests.java +++ b/src/test/java/org/qora/test/forging/RewardTests.java @@ -1,6 +1,7 @@ package org.qora.test.forging; import java.math.BigDecimal; +import java.math.RoundingMode; import java.util.List; import java.util.Map; @@ -74,4 +75,30 @@ public class RewardTests extends Common { } } -} + @Test + public void testProxyReward() throws DataException { + final BigDecimal share = new BigDecimal("12.8"); + + try (final Repository repository = RepositoryManager.getRepository()) { + // Bob needs to make a transaction so his public key is known + AccountUtils.pay(repository, "bob", "chloe", new BigDecimal("1.4444").setScale(8)); + + byte[] proxyPrivateKey = AccountUtils.proxyForging(repository, "alice", "bob", share); + PrivateKeyAccount proxyAccount = new PrivateKeyAccount(repository, proxyPrivateKey); + + Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORA); + BlockGenerator.generateTestingBlock(repository, proxyAccount); + + // We're expected reward * 12.8% to Bob, the rest to Alice + // (first reward is good for first 10 blocks) + BigDecimal firstReward = BlockChain.getInstance().getBlockRewardsByHeight().get(0).reward; + + BigDecimal bobShare = firstReward.multiply(share.movePointLeft(2)).setScale(8, RoundingMode.DOWN); + AccountUtils.assertBalance(repository, "bob", Asset.QORA, initialBalances.get("bob").get(Asset.QORA).add(bobShare)); + + BigDecimal aliceShare = firstReward.subtract(bobShare); + AccountUtils.assertBalance(repository, "alice", Asset.QORA, initialBalances.get("alice").get(Asset.QORA).add(aliceShare)); + } + } + +} \ No newline at end of file