forked from Qortal/qortal
Add API call to list blocks with given generator. +more tests +pad genesis public key
This commit is contained in:
parent
d1c547f24a
commit
93230e9704
@ -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);
|
||||
|
@ -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<BlockData> 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(
|
||||
|
@ -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);
|
||||
|
@ -101,6 +101,11 @@ public interface BlockRepository {
|
||||
*/
|
||||
public int countForgedBlocks(byte[] publicKey) throws DataException;
|
||||
|
||||
/**
|
||||
* Returns blocks with passed generator public key.
|
||||
*/
|
||||
public List<BlockData> getBlocksWithGenerator(byte[] generatorPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||
|
||||
/**
|
||||
* Saves block into repository.
|
||||
*
|
||||
|
@ -158,6 +158,30 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockData> 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> 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");
|
||||
|
@ -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<String, Map<Long, BigDecimal>> getBalances(Repository repository, long... assetIds) throws DataException {
|
||||
Map<String, Map<Long, BigDecimal>> balances = new HashMap<>();
|
||||
|
||||
|
@ -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<String, Map<Long, BigDecimal>> 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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user