Remove "generating balance", and all related aspects/code.

Block forging is now allowed by level 1+ accounts,
instead of accounts with "minting flag" set.
This commit is contained in:
catbref 2019-09-27 15:16:12 +01:00
parent 4c6656dd17
commit 305bb38446
13 changed files with 48 additions and 497 deletions

View File

@ -5,9 +5,7 @@ import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qora.asset.Asset;
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;
@ -53,44 +51,6 @@ public class Account {
return new AccountData(this.address);
}
// More information
/**
* Calculate current generating balance for this account.
* <p>
* This is the current confirmed balance minus amounts received in the last <code>BlockChain.BLOCK_RETARGET_INTERVAL</code> blocks.
*
* @throws DataException
*/
public BigDecimal getGeneratingBalance() throws DataException {
BigDecimal balance = this.getConfirmedBalance(Asset.QORA);
BlockRepository blockRepository = this.repository.getBlockRepository();
BlockData blockData = blockRepository.getLastBlock();
for (int i = 1; i < BlockChain.getInstance().getBlockDifficultyInterval() && blockData != null && blockData.getHeight() > 1; ++i) {
Block block = new Block(this.repository, blockData);
// CIYAM AT transactions should be fetched from repository so no special handling needed here
for (Transaction transaction : block.getTransactions()) {
if (transaction.isInvolved(this)) {
final BigDecimal amount = transaction.getAmount(this);
// Subtract positive amounts only
if (amount.compareTo(BigDecimal.ZERO) > 0)
balance = balance.subtract(amount);
}
}
blockData = block.getParent();
}
// Do not go below 0
balance = balance.max(BigDecimal.ZERO);
return balance;
}
// Balance manipulations - assetId is 0 for QORA
public BigDecimal getBalance(long assetId, int confirmations) throws DataException {

View File

@ -13,8 +13,8 @@ public class Forging {
}
public static boolean canForge(Account account) throws DataException {
Integer flags = account.getFlags();
return flags != null && (flags & getForgingMask()) != 0;
Integer level = account.getLevel();
return level != null && level > 0;
}
}

View File

@ -189,33 +189,6 @@ public class AddressesResource {
}
}
@GET
@Path("/generatingbalance/{address}")
@Operation(
summary = "Return the generating balance of the given address",
description = "Returns the effective balance of the given address, used in Proof-of-Stake calculationgs when generating a new block.",
responses = {
@ApiResponse(
description = "the generating balance",
content = @Content(mediaType = MediaType.TEXT_PLAIN, schema = @Schema(type = "string", format = "number"))
)
}
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
public BigDecimal getGeneratingBalanceOfAddress(@PathParam("address") String address) {
if (!Crypto.isValidAddress(address))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
try (final Repository repository = RepositoryManager.getRepository()) {
Account account = new Account(repository, address);
return account.getGeneratingBalance();
} catch (ApiException e) {
throw e;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@GET
@Path("/balance/{address}")
@Operation(
@ -228,7 +201,7 @@ public class AddressesResource {
}
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE})
public BigDecimal getGeneratingBalance(@PathParam("address") String address) {
public BigDecimal getConfirmedBalance(@PathParam("address") String address) {
if (!Crypto.isValidAddress(address))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
@ -253,7 +226,7 @@ public class AddressesResource {
)
}
)
public String getGeneratingBalance(@PathParam("address") String address, @PathParam("confirmations") int confirmations) {
public String getConfirmedBalance(@PathParam("address") String address, @PathParam("confirmations") int confirmations) {
throw new UnsupportedOperationException();
}

View File

@ -9,7 +9,6 @@ import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@ -28,7 +27,6 @@ import org.qora.api.ApiErrors;
import org.qora.api.ApiException;
import org.qora.api.ApiExceptionFactory;
import org.qora.api.model.BlockForgerSummary;
import org.qora.block.Block;
import org.qora.crypto.Crypto;
import org.qora.data.account.AccountData;
import org.qora.data.block.BlockData;
@ -243,83 +241,6 @@ public class BlocksResource {
}
}
@GET
@Path("/generatingbalance")
@Operation(
summary = "Generating balance of next block",
description = "Calculates the generating balance of the block that will follow the last block",
responses = {
@ApiResponse(
description = "the generating balance",
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
implementation = BigDecimal.class
)
)
)
}
)
@ApiErrors({
ApiError.REPOSITORY_ISSUE
})
public BigDecimal getGeneratingBalance() {
try (final Repository repository = RepositoryManager.getRepository()) {
BlockData blockData = repository.getBlockRepository().getLastBlock();
Block block = new Block(repository, blockData);
return block.calcNextBlockGeneratingBalance();
} catch (ApiException e) {
throw e;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@GET
@Path("/generatingbalance/{signature}")
@Operation(
summary = "Generating balance of block after specific block",
description = "Calculates the generating balance of the block that will follow the block that matches the signature",
responses = {
@ApiResponse(
description = "the block",
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
implementation = BigDecimal.class
)
)
)
}
)
@ApiErrors({
ApiError.INVALID_SIGNATURE, ApiError.BLOCK_NO_EXISTS, ApiError.REPOSITORY_ISSUE
})
public BigDecimal getGeneratingBalance(@PathParam("signature") String signature58) {
// Decode signature
byte[] signature;
try {
signature = Base58.decode(signature58);
} catch (NumberFormatException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_SIGNATURE, e);
}
try (final Repository repository = RepositoryManager.getRepository()) {
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
// Check block exists
if (blockData == null)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.BLOCK_NO_EXISTS);
Block block = new Block(repository, blockData);
return block.calcNextBlockGeneratingBalance();
} catch (ApiException e) {
throw e;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@GET
@Path("/height")
@Operation(

View File

@ -34,7 +34,6 @@ import org.qora.data.block.BlockTransactionData;
import org.qora.data.network.OnlineAccountData;
import org.qora.data.transaction.TransactionData;
import org.qora.repository.ATRepository;
import org.qora.repository.BlockRepository;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.repository.TransactionRepository;
@ -55,28 +54,6 @@ import com.google.common.primitives.Longs;
import io.druid.extendedset.intset.ConciseSet;
/*
* Typical use-case scenarios:
*
* 1. Loading a Block from the database using height, signature, reference, etc.
* 2. Generating a new block, adding unconfirmed transactions
* 3. Receiving a block from another node
*
* Transaction count, transactions signature and total fees need to be maintained by Block.
* In scenario (1) these can be found in database.
* In scenarios (2) and (3) Transactions are added to the Block via addTransaction() method.
* Also in scenarios (2) and (3), Block is responsible for saving Transactions to DB.
*
* When is height set?
* In scenario (1) this can be found in database.
* In scenarios (2) and (3) this will need to be set after successful processing,
* but before Block is saved into database.
*
* GeneratorSignature's data is: reference + generatingBalance + generator's public key
* TransactionSignature's data is: generatorSignature + transaction signatures
* Block signature is: generatorSignature + transactionsSignature
*/
public class Block {
// Validation results
@ -93,7 +70,6 @@ public class Block {
TIMESTAMP_INCORRECT(24),
VERSION_INCORRECT(30),
FEATURE_NOT_YET_RELEASED(31),
GENERATING_BALANCE_INCORRECT(40),
GENERATOR_NOT_ACCEPTED(41),
GENESIS_TRANSACTIONS_INVALID(50),
TRANSACTION_TIMESTAMP_INVALID(51),
@ -144,8 +120,6 @@ public class Block {
/** Locally-generated AT fees */
protected BigDecimal ourAtFees; // Generated locally
/** Cached copy of next block's generating balance */
protected BigDecimal cachedNextGeneratingBalance;
/** Minimum Qora balance for use in calculations. */
public static final BigDecimal MIN_BALANCE = BigDecimal.valueOf(1L).setScale(8);
@ -216,10 +190,7 @@ public class 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 version
* @param reference
* @param timestamp
* @param generatingBalance
* @param parentBlockData
* @param generator
* @throws DataException
*/
@ -231,7 +202,6 @@ public class Block {
int version = parentBlock.getNextBlockVersion();
byte[] reference = parentBlockData.getSignature();
BigDecimal generatingBalance = parentBlock.calcNextBlockGeneratingBalance();
// Fetch our list of online accounts
List<OnlineAccountData> onlineAccounts = Controller.getInstance().getOnlineAccounts();
@ -274,8 +244,7 @@ public class Block {
byte[] generatorSignature;
try {
generatorSignature = generator
.sign(BlockTransformer.getBytesForGeneratorSignature(parentBlockData.getGeneratorSignature(), generatingBalance, generator, encodedOnlineAccounts));
generatorSignature = generator.sign(BlockTransformer.getBytesForGeneratorSignature(parentBlockData.getGeneratorSignature(), generator, encodedOnlineAccounts));
} catch (TransformationException e) {
throw new DataException("Unable to calculate next block generator signature", e);
}
@ -293,7 +262,7 @@ public class Block {
BigDecimal totalFees = atFees;
// This instance used for AT processing
this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp,
generator.getPublicKey(), generatorSignature, atCount, atFees,
encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures);
@ -306,7 +275,7 @@ public class Block {
totalFees = atFees;
// Rebuild blockData using post-AT-execute data
this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
this.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp,
generator.getPublicKey(), generatorSignature, atCount, atFees,
encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures);
}
@ -334,12 +303,10 @@ public class Block {
// Calculate new block timestamp
int version = this.blockData.getVersion();
byte[] reference = this.blockData.getReference();
BigDecimal generatingBalance = this.blockData.getGeneratingBalance();
byte[] generatorSignature;
try {
generatorSignature = generator
.sign(BlockTransformer.getBytesForGeneratorSignature(parentBlockData.getGeneratorSignature(), generatingBalance, generator, this.blockData.getEncodedOnlineAccounts()));
generatorSignature = generator.sign(BlockTransformer.getBytesForGeneratorSignature(parentBlockData.getGeneratorSignature(), generator, this.blockData.getEncodedOnlineAccounts()));
} catch (TransformationException e) {
throw new DataException("Unable to calculate next block generator signature", e);
}
@ -360,7 +327,7 @@ public class Block {
Long onlineAccountsTimestamp = this.blockData.getOnlineAccountsTimestamp();
byte[] onlineAccountsSignatures = this.blockData.getOnlineAccountsSignatures();
newBlock.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
newBlock.blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp,
generator.getPublicKey(), generatorSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures);
// Resign to update transactions signature
@ -412,80 +379,6 @@ public class Block {
return 4;
}
/**
* Return the next block's generating balance.
* <p>
* Every BLOCK_RETARGET_INTERVAL the generating balance is recalculated.
* <p>
* If this block starts a new interval then the new generating balance is calculated, cached and returned.<br>
* Within this interval, the generating balance stays the same so the current block's generating balance will be returned.
*
* @return next block's generating balance
* @throws DataException
*/
public BigDecimal calcNextBlockGeneratingBalance() throws DataException {
if (this.blockData.getHeight() == null)
throw new IllegalStateException("Can't calculate next block's generating balance as this block's height is unset");
final int blockDifficultyInterval = BlockChain.getInstance().getBlockDifficultyInterval();
// This block not at the start of an interval?
if (this.blockData.getHeight() % blockDifficultyInterval != 0)
return this.blockData.getGeneratingBalance();
// Return cached calculation if we have one
if (this.cachedNextGeneratingBalance != null)
return this.cachedNextGeneratingBalance;
// Perform calculation
// Navigate back to first block in previous interval:
// XXX: why can't we simply load using block height?
BlockRepository blockRepo = this.repository.getBlockRepository();
BlockData firstBlock = this.blockData;
try {
for (int i = 1; firstBlock != null && i < blockDifficultyInterval; ++i)
firstBlock = blockRepo.fromSignature(firstBlock.getReference());
} catch (DataException e) {
firstBlock = null;
}
// Couldn't navigate back far enough?
if (firstBlock == null)
throw new IllegalStateException("Failed to calculate next block's generating balance due to lack of historic blocks");
// Calculate the actual time period (in ms) over previous interval's blocks.
long previousGeneratingTime = this.blockData.getTimestamp() - firstBlock.getTimestamp();
// Calculate expected forging time (in ms) for a whole interval based on this block's generating balance.
long expectedGeneratingTime = Block.calcForgingDelay(this.blockData.getGeneratingBalance(), this.blockData.getHeight()) * blockDifficultyInterval;
// Finally, scale generating balance such that faster than expected previous intervals produce larger generating balances.
// NOTE: we have to use doubles and longs here to keep compatibility with Qora v1 results
double multiplier = (double) expectedGeneratingTime / (double) previousGeneratingTime;
long nextGeneratingBalance = (long) (this.blockData.getGeneratingBalance().doubleValue() * multiplier);
this.cachedNextGeneratingBalance = Block.minMaxBalance(BigDecimal.valueOf(nextGeneratingBalance).setScale(8));
return this.cachedNextGeneratingBalance;
}
/**
* Return expected forging delay, in seconds, since previous block based on passed generating balance.
*/
public static long calcForgingDelay(BigDecimal generatingBalance, int previousBlockHeight) {
generatingBalance = Block.minMaxBalance(generatingBalance);
double percentageOfTotal = generatingBalance.divide(BlockChain.getInstance().getMaxBalance()).doubleValue();
BlockTimingByHeight blockTiming = BlockChain.getInstance().getBlockTimingByHeight(previousBlockHeight + 1);
long actualBlockTime = (long) (blockTiming.target + (blockTiming.deviation * (1 - (2 * percentageOfTotal))));
return actualBlockTime;
}
/**
* Return block's transactions.
* <p>
@ -953,10 +846,6 @@ public class Block {
if (this.blockData.getVersion() < 2 && this.blockData.getATCount() != 0)
return ValidationResult.FEATURE_NOT_YET_RELEASED;
// Check generating balance
if (this.blockData.getGeneratingBalance().compareTo(parentBlock.calcNextBlockGeneratingBalance()) != 0)
return ValidationResult.GENERATING_BALANCE_INCORRECT;
// Check generator is allowed to forge this block
if (!isGeneratorValidToForge(parentBlock))
return ValidationResult.GENERATOR_NOT_ACCEPTED;

View File

@ -1,6 +1,7 @@
package org.qora.block;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.MathContext;
@ -160,8 +161,9 @@ public class BlockChain {
unmarshaller.setProperty(UnmarshallerProperties.JSON_INCLUDE_ROOT, false);
} catch (JAXBException e) {
LOGGER.error("Unable to process blockchain config file", e);
throw new RuntimeException("Unable to process blockchain config file", e);
String message = "Failed to setup unmarshaller to process blockchain config file";
LOGGER.error(message, e);
throw new RuntimeException(message, e);
}
BlockChain blockchain = null;
@ -173,8 +175,9 @@ public class BlockChain {
File jsonFile = new File(path + filename);
if (!jsonFile.exists()) {
LOGGER.error("Blockchain config file not found: " + path + filename);
throw new RuntimeException("Blockchain config file not found: " + path + filename);
String message = "Blockchain config file not found: " + path + filename;
LOGGER.error(message);
throw new RuntimeException(message, new FileNotFoundException(message));
}
jsonSource = new StreamSource(jsonFile);
@ -194,14 +197,16 @@ public class BlockChain {
if (linkedException instanceof XMLMarshalException) {
String message = ((XMLMarshalException) linkedException).getInternalException().getLocalizedMessage();
LOGGER.error(message);
throw new RuntimeException(message);
throw new RuntimeException(message, e);
}
LOGGER.error("Unable to process blockchain config file", e);
throw new RuntimeException("Unable to process blockchain config file", e);
String message = "Failed to parse blockchain config file";
LOGGER.error(message, e);
throw new RuntimeException(message, e);
} catch (JAXBException e) {
LOGGER.error("Unable to process blockchain config file", e);
throw new RuntimeException("Unable to process blockchain config file", e);
String message = "Unexpected JAXB issue while processing blockchain config file";
LOGGER.error(message, e);
throw new RuntimeException(message, e);
}
// Validate config

View File

@ -46,7 +46,6 @@ public class GenesisBlock extends Block {
public static class GenesisInfo {
public int version = 1;
public long timestamp;
public BigDecimal generatingBalance;
public TransactionData[] transactions;
@ -123,21 +122,18 @@ public class GenesisBlock extends Block {
}).collect(Collectors.toList());
}
// Minor fix-up
info.generatingBalance.setScale(8);
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, info.generatingBalance, generatorPublicKey);
byte[] bytesForSignature = getBytesForSignature(info.version, reference, generatorPublicKey);
byte[] generatorSignature = calcSignature(bytesForSignature);
byte[] transactionsSignature = generatorSignature;
int height = 1;
int atCount = 0;
BigDecimal atFees = BigDecimal.ZERO.setScale(8);
blockData = new BlockData(info.version, reference, transactionCount, totalFees, transactionsSignature, height, info.timestamp, info.generatingBalance,
blockData = new BlockData(info.version, reference, transactionCount, totalFees, transactionsSignature, height, info.timestamp,
generatorPublicKey, generatorSignature, atCount, atFees);
}
@ -214,7 +210,7 @@ public class GenesisBlock extends Block {
return Bytes.concat(digest, digest);
}
private static byte[] getBytesForSignature(int version, byte[] reference, BigDecimal generatingBalance, byte[] generatorPublicKey) {
private static byte[] getBytesForSignature(int version, byte[] reference, byte[] generatorPublicKey) {
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.
@ -233,8 +229,6 @@ public class GenesisBlock extends Block {
*/
bytes.write(Bytes.ensureCapacity(reference, 64, 0));
bytes.write(Longs.toByteArray(generatingBalance.longValue()));
// 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));
@ -246,8 +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.getGeneratingBalance(),
blockData.getGeneratorPublicKey());
byte[] bytes = getBytesForSignature(blockData.getVersion(), blockData.getReference(), blockData.getGeneratorPublicKey());
return calcSignature(bytes);
}

View File

@ -27,7 +27,6 @@ public class BlockData implements Serializable {
private byte[] transactionsSignature;
private Integer height;
private long timestamp;
private BigDecimal generatingBalance;
private byte[] generatorPublicKey;
private byte[] generatorSignature;
private int atCount;
@ -44,7 +43,7 @@ public class BlockData implements Serializable {
}
public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
BigDecimal generatingBalance, byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees,
byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees,
byte[] encodedOnlineAccounts, int onlineAccountsCount, Long onlineAccountsTimestamp, byte[] onlineAccountsSignatures) {
this.version = version;
this.reference = reference;
@ -53,7 +52,6 @@ public class BlockData implements Serializable {
this.transactionsSignature = transactionsSignature;
this.height = height;
this.timestamp = timestamp;
this.generatingBalance = generatingBalance;
this.generatorPublicKey = generatorPublicKey;
this.generatorSignature = generatorSignature;
this.atCount = atCount;
@ -70,8 +68,8 @@ public class BlockData implements Serializable {
}
public BlockData(int version, byte[] reference, int transactionCount, BigDecimal totalFees, byte[] transactionsSignature, Integer height, long timestamp,
BigDecimal generatingBalance, byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees) {
this(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance, generatorPublicKey, generatorSignature, atCount, atFees,
byte[] generatorPublicKey, byte[] generatorSignature, int atCount, BigDecimal atFees) {
this(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatorPublicKey, generatorSignature, atCount, atFees,
null, 0, null, null);
}
@ -133,10 +131,6 @@ public class BlockData implements Serializable {
return this.timestamp;
}
public BigDecimal getGeneratingBalance() {
return this.generatingBalance;
}
public byte[] getGeneratorPublicKey() {
return this.generatorPublicKey;
}

View File

@ -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, generating_balance, generator, generator_signature, "
+ "transactions_signature, height, generation, generator, generator_signature, "
+ "AT_count, AT_fees, online_accounts, online_accounts_count, online_accounts_timestamp, online_accounts_signatures";
protected HSQLDBRepository repository;
@ -43,17 +43,16 @@ public class HSQLDBBlockRepository implements BlockRepository {
byte[] transactionsSignature = resultSet.getBytes(5);
int height = resultSet.getInt(6);
long timestamp = getZonedTimestampMilli(resultSet, 7);
BigDecimal generatingBalance = resultSet.getBigDecimal(8);
byte[] generatorPublicKey = resultSet.getBytes(9);
byte[] generatorSignature = resultSet.getBytes(10);
int atCount = resultSet.getInt(11);
BigDecimal atFees = resultSet.getBigDecimal(12);
byte[] encodedOnlineAccounts = resultSet.getBytes(13);
int onlineAccountsCount = resultSet.getInt(14);
Long onlineAccountsTimestamp = getZonedTimestampMilli(resultSet, 15);
byte[] onlineAccountsSignatures = resultSet.getBytes(16);
byte[] generatorPublicKey = resultSet.getBytes(8);
byte[] generatorSignature = resultSet.getBytes(9);
int atCount = resultSet.getInt(10);
BigDecimal atFees = resultSet.getBigDecimal(11);
byte[] encodedOnlineAccounts = resultSet.getBytes(12);
int onlineAccountsCount = resultSet.getInt(13);
Long onlineAccountsTimestamp = getZonedTimestampMilli(resultSet, 14);
byte[] onlineAccountsSignatures = resultSet.getBytes(15);
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp,
generatorPublicKey, generatorSignature, atCount, atFees,
encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures);
} catch (SQLException e) {
@ -341,7 +340,7 @@ 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("generating_balance", blockData.getGeneratingBalance())
.bind("generation", toOffsetDateTime(blockData.getTimestamp()))
.bind("generator", blockData.getGeneratorPublicKey()).bind("generator_signature", blockData.getGeneratorSignature())
.bind("AT_count", blockData.getATCount()).bind("AT_fees", blockData.getATFees())
.bind("online_accounts", blockData.getEncodedOnlineAccounts()).bind("online_accounts_count", blockData.getOnlineAccountsCount())

View File

@ -125,7 +125,7 @@ public class HSQLDBDatabaseUpdates {
// Blocks
stmt.execute("CREATE TABLE Blocks (signature BlockSignature, version TINYINT NOT NULL, reference BlockSignature, "
+ "transaction_count INTEGER NOT NULL, total_fees QoraAmount NOT NULL, transactions_signature Signature NOT NULL, "
+ "height INTEGER NOT NULL, generation TIMESTAMP WITH TIME ZONE NOT NULL, generating_balance QoraAmount NOT NULL, "
+ "height INTEGER NOT NULL, generation TIMESTAMP WITH TIME ZONE NOT NULL, "
+ "generator QoraPublicKey NOT NULL, generator_signature Signature NOT NULL, AT_count INTEGER NOT NULL, AT_fees QoraAmount NOT NULL, "
+ "PRIMARY KEY (signature))");
// For finding blocks by height.

View File

@ -37,11 +37,10 @@ public class BlockTransformer extends Transformer {
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 TIMESTAMP_LENGTH = LONG_LENGTH;
private static final int GENERATING_BALANCE_LENGTH = LONG_LENGTH;
private static final int GENERATOR_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 + GENERATING_BALANCE_LENGTH + GENERATOR_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;
public static final int BLOCK_SIGNATURE_LENGTH = GENERATOR_SIGNATURE_LENGTH + TRANSACTIONS_SIGNATURE_LENGTH;
@ -98,8 +97,6 @@ public class BlockTransformer extends Transformer {
byte[] reference = new byte[BLOCK_REFERENCE_LENGTH];
byteBuffer.get(reference);
BigDecimal generatingBalance = BigDecimal.valueOf(byteBuffer.getLong()).setScale(8);
byte[] generatorPublicKey = Serialization.deserializePublicKey(byteBuffer);
byte[] transactionsSignature = new byte[TRANSACTIONS_SIGNATURE_LENGTH];
@ -244,7 +241,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, generatingBalance,
BlockData blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp,
generatorPublicKey, generatorSignature, atCount, atFees, encodedOnlineAccounts, onlineAccountsCount, onlineAccountsTimestamp, onlineAccountsSignatures);
return new Triple<BlockData, List<TransactionData>, List<ATStateData>>(blockData, transactions, atStates);
@ -289,8 +286,6 @@ public class BlockTransformer extends Transformer {
bytes.write(Ints.toByteArray(blockData.getVersion()));
bytes.write(Longs.toByteArray(blockData.getTimestamp()));
bytes.write(blockData.getReference());
// NOTE: generatingBalance serialized as long value, not as BigDecimal, for historic compatibility
bytes.write(Longs.toByteArray(blockData.getGeneratingBalance().longValue()));
bytes.write(blockData.getGeneratorPublicKey());
bytes.write(blockData.getTransactionsSignature());
bytes.write(blockData.getGeneratorSignature());
@ -373,18 +368,16 @@ public class BlockTransformer extends Transformer {
byte[] generatorSignature = getGeneratorSignatureFromReference(blockData.getReference());
PublicKeyAccount generator = new PublicKeyAccount(null, blockData.getGeneratorPublicKey());
return getBytesForGeneratorSignature(generatorSignature, blockData.getGeneratingBalance(), generator, blockData.getEncodedOnlineAccounts());
return getBytesForGeneratorSignature(generatorSignature, generator, blockData.getEncodedOnlineAccounts());
}
public static byte[] getBytesForGeneratorSignature(byte[] generatorSignature, BigDecimal generatingBalance, PublicKeyAccount generator, byte[] encodedOnlineAccounts)
public static byte[] getBytesForGeneratorSignature(byte[] generatorSignature, PublicKeyAccount generator, byte[] encodedOnlineAccounts)
throws TransformationException {
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream(GENERATOR_SIGNATURE_LENGTH + GENERATING_BALANCE_LENGTH + GENERATOR_LENGTH + encodedOnlineAccounts.length);
ByteArrayOutputStream bytes = new ByteArrayOutputStream(GENERATOR_SIGNATURE_LENGTH + GENERATOR_LENGTH + encodedOnlineAccounts.length);
bytes.write(generatorSignature);
bytes.write(Longs.toByteArray(generatingBalance.longValue()));
// 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));

View File

@ -1,109 +0,0 @@
package org.qora.test;
import org.junit.Test;
import org.qora.asset.Asset;
import org.qora.data.at.ATStateData;
import org.qora.data.block.BlockData;
import org.qora.data.block.BlockTransactionData;
import org.qora.data.transaction.BaseTransactionData;
import org.qora.data.transaction.DeployAtTransactionData;
import org.qora.group.Group;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.repository.RepositoryManager;
import org.qora.test.common.Common;
import org.qora.transaction.DeployAtTransaction;
import org.qora.transform.TransformationException;
import org.qora.utils.Base58;
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.util.Arrays;
import com.google.common.hash.HashCode;
public class ATTests extends Common {
@Test
public void testATAccount() throws TransformationException, DataException {
// 2dZ4megUyNoYYY7qWmuSd4xw1yUKgPPF97yBbeddh8aKuC8PLpz7Xvf3r6Zjv1zwGrR8fEAHuaztCPD4KQp76KdL at height 125598
// AT address: AaaUn82XV4YcUtsQ3rHa5ZgqyiK35rVfE3
String expectedAddress = "AaaUn82XV4YcUtsQ3rHa5ZgqyiK35rVfE3";
byte[] creatorPublicKey = HashCode.fromString("c74d71ecec6b37890f26573186e634986cc90a507af01749f92aa2c7c95ad05f").asBytes();
String name = "QORABURST @ 1.00";
String description = "Initiators BURST address: BURST-LKGW-Z2JK-EZ99-E7CUE";
String ATType = "acct";
String tags = "acct,atomic cross chain tx,initiate,initiator";
byte[] creationBytes = HashCode
.fromString("010000000100010000000000" + "0094357700" + "000000bf"
+ "3501030900000006040000000900000029302009000000040000000f1ab4000000330403090000003525010a000000260a000000320903350703090000003526010a0000001b0a000000cd322801331601000000003317010100000033180102000000331901030000003505020a0000001b0a000000a1320b033205041e050000001833000509000000320a033203041ab400000033160105000000331701060000003318010700000033190108000000320304320b033203041ab7"
+ "00000048"
+ "5e211280259d2f3130248482c2dfc53be2fd5f9bedc9bc21425f951e8097a21900000000c80000003ac8716ad810191acf270d22e9f47f27806256c10d6ba6144900000000000000")
.asBytes();
BigDecimal amount = BigDecimal.valueOf(500.0).setScale(8);
BigDecimal fee = BigDecimal.valueOf(20.0).setScale(8);
long timestamp = 1439997077932L;
byte[] reference = Base58.decode("2D3jX1pEgu6irsQ7QzJb85QP1D9M45dNyP5M9a3WFHndU5ZywF4F5pnUurcbzMnGMcTwpAY6H7DuLw8cUBU66ao1");
byte[] signature = Base58.decode("2dZ4megUyNoYYY7qWmuSd4xw1yUKgPPF97yBbeddh8aKuC8PLpz7Xvf3r6Zjv1zwGrR8fEAHuaztCPD4KQp76KdL");
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, Group.NO_GROUP, reference, creatorPublicKey, fee, signature);
DeployAtTransactionData transactionData = new DeployAtTransactionData(baseTransactionData, name, description, ATType, tags, creationBytes, amount, Asset.QORA);
try (final Repository repository = RepositoryManager.getRepository()) {
repository.getTransactionRepository().save(transactionData);
DeployAtTransaction transaction = new DeployAtTransaction(repository, transactionData);
// Fake entry for this transaction at block height 125598 if it doesn't already exist
if (transaction.getHeight() == 0) {
byte[] blockSignature = Base58.decode(
"2amu634LnAbxeLfDtWdTLiCWtKu1XM2XLK9o6fDM7yGNNoh5Tq2KxSLdx8AS486zUU1wYNGCm8mcGxjMiww979MxdPVB2PQzaKrW2aFn9hpdSNN6Nk7EmeYKwsZdx9tkpHfBt5thSrUUrhzXJju9KYCAP6p3Ty4zccFkaxCP15j332U");
byte[] generatorSignature = Arrays.copyOfRange(blockSignature, 0, 64);
byte[] transactionsSignature = Arrays.copyOfRange(blockSignature, 64, 128);
// Check block exists too
if (repository.getBlockRepository().fromSignature(blockSignature) == null) {
int version = 2;
byte[] blockReference = blockSignature;
int transactionCount = 0;
BigDecimal totalFees = BigDecimal.valueOf(70.0).setScale(8);
int height = 125598;
long blockTimestamp = 1439997158336L;
BigDecimal generatingBalance = BigDecimal.valueOf(1440368826L).setScale(8);
byte[] generatorPublicKey = Base58.decode("X4s833bbtghh7gejmaBMbWqD44HrUobw93ANUuaNhFc");
int atCount = 1;
BigDecimal atFees = BigDecimal.valueOf(50.0).setScale(8);
BlockData blockData = new BlockData(version, blockReference, transactionCount, totalFees, transactionsSignature, height, blockTimestamp,
generatingBalance, generatorPublicKey, generatorSignature, atCount, atFees);
repository.getBlockRepository().save(blockData);
byte[] atBytes = HashCode.fromString("17950a6c62d17ff0caa545651c054a105f1c464daca443df846cc6a3d58f764b78c09cff50f0fd9ec2").asBytes();
String atAddress = Base58.encode(Arrays.copyOfRange(atBytes, 0, 25));
byte[] stateHash = Arrays.copyOfRange(atBytes, 25, atBytes.length);
ATStateData atStateData = new ATStateData(atAddress, height, timestamp, new byte[0], stateHash, atFees);
repository.getATRepository().save(atStateData);
}
int sequence = 0;
BlockTransactionData blockTransactionData = new BlockTransactionData(blockSignature, sequence, signature);
repository.getBlockRepository().save(blockTransactionData);
}
String actualAddress = transaction.getATAccount().getAddress();
repository.discardChanges();
assertEquals(expectedAddress, actualAddress);
}
}
}

View File

@ -1,67 +0,0 @@
package org.qora.test;
import org.junit.Test;
import org.qora.account.PrivateKeyAccount;
import org.qora.block.Block;
import org.qora.block.GenesisBlock;
import org.qora.data.block.BlockData;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
import org.qora.repository.RepositoryManager;
import org.qora.test.common.Common;
import org.qora.utils.Base58;
import static org.junit.Assert.*;
import java.math.BigDecimal;
public class SignatureTests extends Common {
@Test
public void testGenesisBlockSignature() throws DataException {
String expected58 = "6pHMBFif7jXFG654joT8GPaymau1fMtaxacRyqSrnAwQMQDvqRuLpHpfFyqX4gWVvj4pF1mwQhFgqWAvjVvPJUjmBZQvL751dM9cEcQBTaUcxtNLuWZCVUAtbnWN9f7FsLppHhkPbxwpoodL3UJYRGt3EZrG17mhv1RJbmq8j6rr7Mk";
try (final Repository repository = RepositoryManager.getRepository()) {
GenesisBlock block = GenesisBlock.getInstance(repository);
BlockData blockData = block.getBlockData();
System.out
.println("Generator: " + block.getGenerator().getAddress() + ", generator signature: " + Base58.encode(blockData.getGeneratorSignature()));
assertEquals(expected58, Base58.encode(block.getSignature()));
}
}
@Test
public void testBlockSignature() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
PrivateKeyAccount generator = new PrivateKeyAccount(repository,
new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 });
int version = 3;
byte[] reference = Base58.decode(
"BSfgEr6r1rXGGJCv8criR5NcBWfpHdJnm9x5unPwxvojEKCESv1wH1zJm7yvCeC48wshymYtARbHdUojbqWCCWW7h2UTc8g5oEx59C9M41dM7H48My8gVkcEZdxR1of3VgpE5UcowFp3kFC12hVcD9hUttJ2i2nZWMwprbFtUGyVv1U");
int transactionCount = 0;
BigDecimal totalFees = BigDecimal.ZERO.setScale(8);
byte[] transactionsSignature = null;
int height = 0;
long timestamp = System.currentTimeMillis() - 5000;
BigDecimal generatingBalance = BigDecimal.valueOf(10_000_000L).setScale(8);
byte[] generatorPublicKey = generator.getPublicKey();
byte[] generatorSignature = null;
int atCount = 0;
BigDecimal atFees = BigDecimal.valueOf(10_000_000L).setScale(8);
BlockData blockData = new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
generatorPublicKey, generatorSignature, atCount, atFees);
Block block = new Block(repository, blockData, generator);
block.sign();
assertTrue(block.isSignatureValid());
}
}
}