forked from Qortal/qortal
More repository work
Moved more repository-like methods from qora.* classes to repository. Removed qora.block.BlockTransaction as it's pretty much internal to the HSQLDB repository. Fixing qora.account.* Fixing genesis-related classes: block, account, transaction...
This commit is contained in:
parent
37d9bcbb53
commit
698c4b6cc9
@ -14,7 +14,8 @@ public class GenesisTransactionData extends TransactionData {
|
||||
// Constructors
|
||||
|
||||
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp, byte[] signature) {
|
||||
super(TransactionType.GENESIS, BigDecimal.ZERO, new GenesisAccount().getPublicKey(), timestamp, signature);
|
||||
// Zero fee
|
||||
super(TransactionType.GENESIS, BigDecimal.ZERO, GenesisAccount.PUBLIC_KEY, timestamp, signature);
|
||||
|
||||
this.recipient = recipient;
|
||||
this.amount = amount;
|
||||
|
@ -19,9 +19,7 @@ public class Account {
|
||||
|
||||
public Account(Repository repository, String address) throws DataException {
|
||||
this.repository = repository;
|
||||
this.accountData = this.repository.getAccountRepository().getAccount(address);
|
||||
if (this.accountData == null)
|
||||
this.accountData = new AccountData(address);
|
||||
this.accountData = new AccountData(address);
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
|
@ -1,9 +1,14 @@
|
||||
package qora.account;
|
||||
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
|
||||
public final class GenesisAccount extends PublicKeyAccount {
|
||||
|
||||
public GenesisAccount() {
|
||||
super(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 };
|
||||
|
||||
public GenesisAccount(Repository repository) throws DataException {
|
||||
super(repository, PUBLIC_KEY);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
package qora.account;
|
||||
|
||||
import data.account.AccountData;
|
||||
import qora.crypto.Crypto;
|
||||
import qora.crypto.Ed25519;
|
||||
import repository.Repository;
|
||||
import utils.Pair;
|
||||
|
||||
public class PrivateKeyAccount extends PublicKeyAccount {
|
||||
@ -15,11 +17,12 @@ public class PrivateKeyAccount extends PublicKeyAccount {
|
||||
* @param seed
|
||||
* byte[32] used to create private/public key pair
|
||||
*/
|
||||
public PrivateKeyAccount(byte[] seed) {
|
||||
public PrivateKeyAccount(Repository repository, byte[] seed) {
|
||||
this.repository = repository;
|
||||
this.seed = seed;
|
||||
this.keyPair = Ed25519.createKeyPair(seed);
|
||||
this.publicKey = keyPair.getB();
|
||||
this.address = Crypto.toAddress(this.publicKey);
|
||||
this.accountData = new AccountData(Crypto.toAddress(this.publicKey));
|
||||
}
|
||||
|
||||
public byte[] getSeed() {
|
||||
|
@ -2,13 +2,15 @@ package qora.account;
|
||||
|
||||
import qora.crypto.Crypto;
|
||||
import qora.crypto.Ed25519;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
|
||||
public class PublicKeyAccount extends Account {
|
||||
|
||||
protected byte[] publicKey;
|
||||
|
||||
public PublicKeyAccount(byte[] publicKey) {
|
||||
super(Crypto.toAddress(publicKey));
|
||||
public PublicKeyAccount(Repository repository, byte[] publicKey) throws DataException {
|
||||
super(repository, Crypto.toAddress(publicKey));
|
||||
|
||||
this.publicKey = publicKey;
|
||||
}
|
||||
@ -28,4 +30,8 @@ public class PublicKeyAccount extends Account {
|
||||
}
|
||||
}
|
||||
|
||||
public static String getAddress(byte[] publicKey) {
|
||||
return Crypto.toAddress(publicKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -49,10 +49,10 @@ import utils.NTP;
|
||||
public class Block {
|
||||
|
||||
// Properties
|
||||
private Repository repository;
|
||||
private BlockData blockData;
|
||||
private PublicKeyAccount generator;
|
||||
|
||||
protected Repository repository;
|
||||
protected BlockData blockData;
|
||||
protected PublicKeyAccount generator;
|
||||
|
||||
// Other properties
|
||||
protected List<Transaction> transactions;
|
||||
protected BigDecimal cachedNextGeneratingBalance;
|
||||
@ -76,10 +76,10 @@ public class Block {
|
||||
|
||||
// Constructors
|
||||
|
||||
public Block(Repository repository, BlockData blockData) {
|
||||
public Block(Repository repository, BlockData blockData) throws DataException {
|
||||
this.repository = repository;
|
||||
this.blockData = blockData;
|
||||
this.generator = new PublicKeyAccount(blockData.getGeneratorPublicKey());
|
||||
this.generator = new PublicKeyAccount(repository, blockData.getGeneratorPublicKey());
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
@ -88,6 +88,10 @@ public class Block {
|
||||
return this.blockData;
|
||||
}
|
||||
|
||||
public PublicKeyAccount getGenerator() {
|
||||
return this.generator;
|
||||
}
|
||||
|
||||
// More information
|
||||
|
||||
/**
|
||||
@ -142,7 +146,7 @@ public class Block {
|
||||
// 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 < BLOCK_RETARGET_INTERVAL; ++i)
|
||||
firstBlock = blockRepo.fromSignature(firstBlock.getReference());
|
||||
@ -200,10 +204,10 @@ public class Block {
|
||||
throw new IllegalStateException("Block's transactions from repository do not match block's transaction count");
|
||||
|
||||
this.transactions = new ArrayList<Transaction>();
|
||||
|
||||
|
||||
for (TransactionData transactionData : transactionsData)
|
||||
this.transactions.add(Transaction.fromData(transactionData));
|
||||
|
||||
|
||||
return this.transactions;
|
||||
}
|
||||
|
||||
@ -294,7 +298,6 @@ public class Block {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isSignatureValid() {
|
||||
try {
|
||||
// Check generator's signature first
|
||||
@ -320,7 +323,7 @@ public class Block {
|
||||
*
|
||||
* @return true if block is valid, false otherwise.
|
||||
* @throws SQLException
|
||||
* @throws DataException
|
||||
* @throws DataException
|
||||
*/
|
||||
public boolean isValid() throws SQLException, DataException {
|
||||
// TODO
|
||||
@ -334,7 +337,7 @@ public class Block {
|
||||
return false;
|
||||
|
||||
Block parentBlock = new Block(this.repository, parentBlockData);
|
||||
|
||||
|
||||
// Check timestamp is valid, i.e. later than parent timestamp and not in the future, within ~500ms margin
|
||||
if (this.blockData.getTimestamp() < parentBlockData.getTimestamp() || this.blockData.getTimestamp() - BLOCK_TIMESTAMP_MARGIN > NTP.getTime())
|
||||
return false;
|
||||
@ -410,7 +413,7 @@ public class Block {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void process() throws DataException, SQLException {
|
||||
public void process() throws DataException {
|
||||
// Process transactions (we'll link them to this block after saving the block itself)
|
||||
List<Transaction> transactions = this.getTransactions();
|
||||
for (Transaction transaction : transactions)
|
||||
@ -422,7 +425,7 @@ public class Block {
|
||||
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(blockFee));
|
||||
|
||||
// Link block into blockchain by fetching signature of highest block and setting that as our reference
|
||||
int blockchainHeight = BlockChain.getHeight();
|
||||
int blockchainHeight = this.repository.getBlockRepository().getBlockchainHeight();
|
||||
BlockData latestBlockData = this.repository.getBlockRepository().fromHeight(blockchainHeight);
|
||||
if (latestBlockData != null)
|
||||
this.blockData.setReference(latestBlockData.getSignature());
|
||||
@ -435,7 +438,8 @@ public class Block {
|
||||
Transaction transaction = transactions.get(sequence);
|
||||
|
||||
// Link transaction to this block
|
||||
BlockTransactionData blockTransactionData = new BlockTransactionData(this.getSignature(), sequence, transaction.getTransactionData().getSignature());
|
||||
BlockTransactionData blockTransactionData = new BlockTransactionData(this.getSignature(), sequence,
|
||||
transaction.getTransactionData().getSignature());
|
||||
this.repository.getBlockRepository().save(blockTransactionData);
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
package qora.block;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import database.DB;
|
||||
import data.block.BlockData;
|
||||
import qora.assets.Asset;
|
||||
import repository.BlockRepository;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
|
||||
/**
|
||||
* Class representing the blockchain as a whole.
|
||||
@ -35,69 +38,43 @@ public class BlockChain {
|
||||
*
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static void validate() throws SQLException {
|
||||
public static void validate() throws DataException {
|
||||
// Check first block is Genesis Block
|
||||
if (!isGenesisBlockValid())
|
||||
rebuildBlockchain();
|
||||
}
|
||||
|
||||
private static boolean isGenesisBlockValid() throws SQLException {
|
||||
int blockchainHeight = getHeight();
|
||||
private static boolean isGenesisBlockValid() throws DataException {
|
||||
BlockRepository blockRepository = RepositoryManager.getRepository().getBlockRepository();
|
||||
|
||||
int blockchainHeight = blockRepository.getBlockchainHeight();
|
||||
if (blockchainHeight < 1)
|
||||
return false;
|
||||
|
||||
Block block = Block.fromHeight(1);
|
||||
if (block == null)
|
||||
BlockData blockData = blockRepository.fromHeight(1);
|
||||
if (blockData == null)
|
||||
return false;
|
||||
|
||||
return GenesisBlock.isGenesisBlock(block);
|
||||
return GenesisBlock.isGenesisBlock(blockData);
|
||||
}
|
||||
|
||||
private static void rebuildBlockchain() throws SQLException {
|
||||
// (Re)build database
|
||||
DB.rebuild();
|
||||
private static void rebuildBlockchain() throws DataException {
|
||||
// (Re)build repository
|
||||
Repository repository = RepositoryManager.getRepository();
|
||||
repository.rebuild();
|
||||
|
||||
// Add Genesis Block
|
||||
GenesisBlock genesisBlock = GenesisBlock.getInstance();
|
||||
GenesisBlock genesisBlock = new GenesisBlock(repository);
|
||||
genesisBlock.process();
|
||||
|
||||
// Add QORA asset.
|
||||
// NOTE: Asset's transaction reference is Genesis Block's generator signature which doesn't exist as a transaction!
|
||||
// TODO construct Asset(repository, AssetData) then .save()?
|
||||
Asset qoraAsset = new Asset(Asset.QORA, genesisBlock.getGenerator().getAddress(), "Qora", "This is the simulated Qora asset.", 10_000_000_000L, true,
|
||||
genesisBlock.getGeneratorSignature());
|
||||
genesisBlock.getBlockData().getGeneratorSignature());
|
||||
qoraAsset.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return block height from DB using signature.
|
||||
*
|
||||
* @param signature
|
||||
* @return height, or 0 if block not found.
|
||||
* @throws SQLException
|
||||
*/
|
||||
public static int getBlockHeightFromSignature(byte[] signature) throws SQLException {
|
||||
ResultSet rs = DB.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature);
|
||||
if (rs == null)
|
||||
return 0;
|
||||
|
||||
return rs.getInt(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return highest block height from DB.
|
||||
*
|
||||
* @return height, or 0 if there are no blocks in DB (not very likely).
|
||||
*/
|
||||
public static int getHeight() {
|
||||
try {
|
||||
ResultSet rs = DB.checkedExecute("SELECT MAX(height) FROM Blocks");
|
||||
if (rs == null)
|
||||
return 0;
|
||||
|
||||
return rs.getInt(1);
|
||||
} catch (SQLException e) {
|
||||
return 0;
|
||||
}
|
||||
repository.saveChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +0,0 @@
|
||||
package qora.block;
|
||||
|
||||
public class BlockTransaction {
|
||||
|
||||
}
|
@ -3,37 +3,34 @@ package qora.block;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
import data.block.BlockData;
|
||||
import data.transaction.GenesisTransactionData;
|
||||
import data.transaction.TransactionData;
|
||||
import qora.account.GenesisAccount;
|
||||
import qora.crypto.Crypto;
|
||||
import qora.transaction.GenesisTransaction;
|
||||
import qora.transaction.TransactionHandler;
|
||||
import qora.transaction.Transaction;
|
||||
import repository.DataException;
|
||||
import repository.Repository;
|
||||
|
||||
public class GenesisBlock extends Block {
|
||||
|
||||
private static GenesisBlock instance;
|
||||
|
||||
private static final int GENESIS_BLOCK_VERSION = 1;
|
||||
private static final byte[] GENESIS_REFERENCE = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1 }; // NOTE: Neither 64 nor 128 bytes!
|
||||
private static final BigDecimal GENESIS_GENERATING_BALANCE = BigDecimal.valueOf(10_000_000L).setScale(8);
|
||||
private static final GenesisAccount GENESIS_GENERATOR = new GenesisAccount();
|
||||
private static final byte[] GENESIS_GENERATOR_PUBLIC_KEY = GenesisAccount.PUBLIC_KEY; // NOTE: 8 bytes not 32 bytes!
|
||||
private static final long GENESIS_TIMESTAMP = 1400247274336L; // QORA RELEASE: Fri May 16 13:34:34.336 2014 UTC
|
||||
private static final byte[] GENESIS_GENERATOR_SIGNATURE = calcSignature();
|
||||
private static final byte[] GENESIS_TRANSACTIONS_SIGNATURE = calcSignature();
|
||||
|
||||
// Constructors
|
||||
protected GenesisBlock() {
|
||||
super(GENESIS_BLOCK_VERSION, GENESIS_REFERENCE, GENESIS_TIMESTAMP, GENESIS_GENERATING_BALANCE, GENESIS_GENERATOR, GENESIS_GENERATOR_SIGNATURE,
|
||||
GENESIS_TRANSACTIONS_SIGNATURE, null, null, new ArrayList<TransactionHandler>());
|
||||
|
||||
this.height = 1;
|
||||
public GenesisBlock(Repository repository) throws DataException {
|
||||
super(repository, new BlockData(GENESIS_BLOCK_VERSION, GENESIS_REFERENCE, 0, BigDecimal.ZERO.setScale(8), GENESIS_TRANSACTIONS_SIGNATURE, 1,
|
||||
GENESIS_TIMESTAMP, GENESIS_GENERATING_BALANCE, GENESIS_GENERATOR_PUBLIC_KEY, GENESIS_GENERATOR_SIGNATURE, null, null));
|
||||
|
||||
// Genesis transactions
|
||||
addGenesisTransaction("QUD9y7NZqTtNwvSAUfewd7zKUGoVivVnTW", "7032468.191");
|
||||
@ -172,74 +169,35 @@ public class GenesisBlock extends Block {
|
||||
addGenesisTransaction("QT79PhvBwE6vFzfZ4oh5wdKVsEazZuVJFy", "6360421.343");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return cached GenesisBlock to save constructing one from scratch.
|
||||
*
|
||||
* @return GenesisBlock
|
||||
*/
|
||||
public static GenesisBlock getInstance() {
|
||||
if (instance == null)
|
||||
instance = new GenesisBlock();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
// Getters/setters
|
||||
|
||||
// More information
|
||||
|
||||
public static boolean isGenesisBlock(Block block) {
|
||||
if (block.height != 1)
|
||||
public static boolean isGenesisBlock(BlockData blockData) {
|
||||
if (blockData.getHeight() != 1)
|
||||
return false;
|
||||
|
||||
// Validate block signature
|
||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, block.generatorSignature))
|
||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, blockData.getGeneratorSignature()))
|
||||
return false;
|
||||
|
||||
// Validate transactions signature
|
||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, block.transactionsSignature))
|
||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, blockData.getTransactionsSignature()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load/Save
|
||||
|
||||
protected GenesisBlock(byte[] signature) throws SQLException {
|
||||
super(signature);
|
||||
}
|
||||
|
||||
protected GenesisBlock(ResultSet rs) throws SQLException {
|
||||
super(rs);
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
/**
|
||||
* Refuse to load parent of GenesisBlock from DB!
|
||||
* <p>
|
||||
* As the genesis block is the first block, this always returns null.
|
||||
*
|
||||
* @return null
|
||||
* @throws SQLException
|
||||
*/
|
||||
@Override
|
||||
public Block getParent() throws SQLException {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Converters
|
||||
|
||||
// Processing
|
||||
|
||||
@Override
|
||||
public boolean addTransaction(TransactionHandler transaction) {
|
||||
public boolean addTransaction(TransactionData transactionData) {
|
||||
// The genesis block has a fixed set of transactions so always refuse.
|
||||
return false;
|
||||
}
|
||||
|
||||
private void addGenesisTransaction(String recipient, String amount) {
|
||||
this.transactions.add(new GenesisTransaction(recipient, new BigDecimal(amount).setScale(8), this.timestamp));
|
||||
this.transactions
|
||||
.add(Transaction.fromData(new GenesisTransactionData(recipient, new BigDecimal(amount).setScale(8), this.getBlockData().getTimestamp())));
|
||||
this.blockData.setTransactionCount(this.blockData.getTransactionCount() + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -288,7 +246,7 @@ public class GenesisBlock extends Block {
|
||||
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.
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(8 + 64 + GENERATING_BALANCE_LENGTH + GENERATOR_LENGTH);
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream(8 + 64 + 8 + 32);
|
||||
|
||||
/*
|
||||
* NOTE: Historic code had genesis block using Longs.toByteArray() compared to standard block's Ints.toByteArray. The subsequent
|
||||
@ -306,7 +264,7 @@ public class GenesisBlock extends Block {
|
||||
bytes.write(Longs.toByteArray(GENESIS_GENERATING_BALANCE.longValue()));
|
||||
|
||||
// NOTE: Genesis account's public key is only 8 bytes, not the usual 32, so we have to pad.
|
||||
bytes.write(Bytes.ensureCapacity(GENESIS_GENERATOR.getPublicKey(), 32, 0));
|
||||
bytes.write(Bytes.ensureCapacity(GENESIS_GENERATOR_PUBLIC_KEY, 32, 0));
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException e) {
|
||||
@ -317,25 +275,25 @@ public class GenesisBlock extends Block {
|
||||
@Override
|
||||
public boolean isSignatureValid() {
|
||||
// Validate block signature
|
||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, this.generatorSignature))
|
||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, this.getBlockData().getGeneratorSignature()))
|
||||
return false;
|
||||
|
||||
// Validate transactions signature
|
||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, this.transactionsSignature))
|
||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, this.getBlockData().getTransactionsSignature()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() throws SQLException {
|
||||
public boolean isValid() throws DataException {
|
||||
// Check there is no other block in DB
|
||||
if (BlockChain.getHeight() != 0)
|
||||
if (this.repository.getBlockRepository().getBlockchainHeight() != 0)
|
||||
return false;
|
||||
|
||||
// Validate transactions
|
||||
for (TransactionHandler transaction : this.getTransactions())
|
||||
if (transaction.isValid() != TransactionHandler.ValidationResult.OK)
|
||||
for (Transaction transaction : this.getTransactions())
|
||||
if (transaction.isValid() != Transaction.ValidationResult.OK)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -12,6 +12,7 @@ import data.transaction.TransactionData;
|
||||
import qora.account.PrivateKeyAccount;
|
||||
import qora.block.Block;
|
||||
import qora.block.BlockChain;
|
||||
import repository.Repository;
|
||||
import repository.RepositoryManager;
|
||||
import settings.Settings;
|
||||
import transform.TransformationException;
|
||||
@ -63,26 +64,27 @@ public abstract class Transaction {
|
||||
protected static final BigDecimal maxBytePerFee = BigDecimal.valueOf(Settings.getInstance().getMaxBytePerFee());
|
||||
protected static final BigDecimal minFeePerByte = BigDecimal.ONE.divide(maxBytePerFee, MathContext.DECIMAL32);
|
||||
|
||||
// Properties
|
||||
protected TransactionData transactionData;
|
||||
|
||||
|
||||
// Constructors
|
||||
|
||||
public static Transaction fromData(TransactionData transactionData) {
|
||||
switch (transactionData.getType()) {
|
||||
case GENESIS:
|
||||
return new GenesisTransaction(transactionData);
|
||||
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Getters / Setters
|
||||
|
||||
|
||||
public TransactionData getTransactionData() {
|
||||
return this.transactionData;
|
||||
}
|
||||
|
||||
|
||||
// More information
|
||||
|
||||
public long getDeadline() {
|
||||
@ -108,17 +110,18 @@ public abstract class Transaction {
|
||||
|
||||
public BigDecimal calcRecommendedFee() {
|
||||
try {
|
||||
BigDecimal recommendedFee = BigDecimal.valueOf(TransactionTransformer.getDataLength(this.transactionData)).divide(maxBytePerFee, MathContext.DECIMAL32).setScale(8);
|
||||
|
||||
BigDecimal recommendedFee = BigDecimal.valueOf(TransactionTransformer.getDataLength(this.transactionData))
|
||||
.divide(maxBytePerFee, MathContext.DECIMAL32).setScale(8);
|
||||
|
||||
// security margin
|
||||
recommendedFee = recommendedFee.add(new BigDecimal("0.0000001"));
|
||||
|
||||
|
||||
if (recommendedFee.compareTo(MINIMUM_FEE) <= 0) {
|
||||
recommendedFee = MINIMUM_FEE;
|
||||
} else {
|
||||
recommendedFee = recommendedFee.setScale(0, BigDecimal.ROUND_UP);
|
||||
}
|
||||
|
||||
|
||||
return recommendedFee.setScale(8);
|
||||
} catch (TransformationException e) {
|
||||
throw new IllegalStateException("Unable to get transaction byte length?");
|
||||
@ -139,7 +142,7 @@ public abstract class Transaction {
|
||||
* @return height, or 0 if not in blockchain (i.e. unconfirmed)
|
||||
*/
|
||||
public int getHeight() {
|
||||
return RepositoryManager.getTransactionRepository().getHeight(this.transactionData);
|
||||
return RepositoryManager.getRepository().getTransactionRepository().getHeight(this.transactionData);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -148,14 +151,14 @@ public abstract class Transaction {
|
||||
* @return confirmation count, or 0 if not in blockchain (i.e. unconfirmed)
|
||||
*/
|
||||
public int getConfirmations() {
|
||||
int ourHeight = this.getHeight();
|
||||
int ourHeight = getHeight();
|
||||
if (ourHeight == 0)
|
||||
return 0;
|
||||
|
||||
int blockChainHeight = BlockChain.getHeight();
|
||||
if (blockChainHeight == 0)
|
||||
return 0;
|
||||
|
||||
|
||||
return blockChainHeight - ourHeight + 1;
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,22 @@ public interface BlockRepository {
|
||||
|
||||
public BlockData fromHeight(int height) throws DataException;
|
||||
|
||||
/**
|
||||
* Return height of block in blockchain using block's signature.
|
||||
*
|
||||
* @param signature
|
||||
* @return height, or 0 if not found in blockchain.
|
||||
* @throws DataException
|
||||
*/
|
||||
public int getHeightFromSignature(byte[] signature) throws DataException;
|
||||
|
||||
/**
|
||||
* Return highest block height from DB.
|
||||
*
|
||||
* @return height, or 0 if there are no blocks in DB (not very likely).
|
||||
*/
|
||||
public int getBlockchainHeight() throws DataException;
|
||||
|
||||
public List<TransactionData> getTransactionsFromSignature(byte[] signature) throws DataException;
|
||||
|
||||
public void save(BlockData blockData) throws DataException;
|
||||
|
@ -14,4 +14,6 @@ public interface Repository {
|
||||
|
||||
public void close() throws DataException;
|
||||
|
||||
public void rebuild() throws DataException;
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,31 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
private BlockData getBlockFromResultSet(ResultSet rs) throws DataException {
|
||||
if (rs == null)
|
||||
return null;
|
||||
|
||||
try {
|
||||
int version = rs.getInt(1);
|
||||
byte[] reference = this.repository.getResultSetBytes(rs.getBinaryStream(2));
|
||||
int transactionCount = rs.getInt(3);
|
||||
BigDecimal totalFees = rs.getBigDecimal(4);
|
||||
byte[] transactionsSignature = this.repository.getResultSetBytes(rs.getBinaryStream(5));
|
||||
int height = rs.getInt(6);
|
||||
long timestamp = rs.getTimestamp(7).getTime();
|
||||
BigDecimal generatingBalance = rs.getBigDecimal(8);
|
||||
byte[] generatorPublicKey = this.repository.getResultSetBytes(rs.getBinaryStream(9));
|
||||
byte[] generatorSignature = this.repository.getResultSetBytes(rs.getBinaryStream(10));
|
||||
byte[] atBytes = this.repository.getResultSetBytes(rs.getBinaryStream(11));
|
||||
BigDecimal atFees = rs.getBigDecimal(12);
|
||||
|
||||
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
|
||||
generatorPublicKey, generatorSignature, atBytes, atFees);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Error extracting data from result set", e);
|
||||
}
|
||||
}
|
||||
|
||||
public BlockData fromSignature(byte[] signature) throws DataException {
|
||||
try {
|
||||
ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature);
|
||||
@ -52,28 +77,27 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
||||
}
|
||||
}
|
||||
|
||||
private BlockData getBlockFromResultSet(ResultSet rs) throws DataException {
|
||||
if (rs == null)
|
||||
return null;
|
||||
|
||||
public int getHeightFromSignature(byte[] signature) throws DataException {
|
||||
try {
|
||||
int version = rs.getInt(1);
|
||||
byte[] reference = this.repository.getResultSetBytes(rs.getBinaryStream(2));
|
||||
int transactionCount = rs.getInt(3);
|
||||
BigDecimal totalFees = rs.getBigDecimal(4);
|
||||
byte[] transactionsSignature = this.repository.getResultSetBytes(rs.getBinaryStream(5));
|
||||
int height = rs.getInt(6);
|
||||
long timestamp = rs.getTimestamp(7).getTime();
|
||||
BigDecimal generatingBalance = rs.getBigDecimal(8);
|
||||
byte[] generatorPublicKey = this.repository.getResultSetBytes(rs.getBinaryStream(9));
|
||||
byte[] generatorSignature = this.repository.getResultSetBytes(rs.getBinaryStream(10));
|
||||
byte[] atBytes = this.repository.getResultSetBytes(rs.getBinaryStream(11));
|
||||
BigDecimal atFees = rs.getBigDecimal(12);
|
||||
ResultSet rs = this.repository.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature);
|
||||
if (rs == null)
|
||||
return 0;
|
||||
|
||||
return new BlockData(version, reference, transactionCount, totalFees, transactionsSignature, height, timestamp, generatingBalance,
|
||||
generatorPublicKey, generatorSignature, atBytes, atFees);
|
||||
return rs.getInt(1);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Error extracting data from result set", e);
|
||||
throw new DataException("Error obtaining block height from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getBlockchainHeight() throws DataException {
|
||||
try {
|
||||
ResultSet rs = this.repository.checkedExecute("SELECT MAX(height) FROM Blocks");
|
||||
if (rs == null)
|
||||
return 0;
|
||||
|
||||
return rs.getInt(1);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Error obtaining blockchain height from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,10 @@ public class HSQLDBRepository implements Repository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rebuild() throws DataException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert InputStream, from ResultSet.getBinaryStream(), into byte[].
|
||||
*
|
||||
|
@ -210,7 +210,7 @@ public class BlockTransformer extends Transformer {
|
||||
json.put("version", blockData.getVersion());
|
||||
json.put("timestamp", blockData.getTimestamp());
|
||||
json.put("generatingBalance", blockData.getGeneratingBalance());
|
||||
json.put("generator", new PublicKeyAccount(blockData.getGeneratorPublicKey()).getAddress());
|
||||
json.put("generator", PublicKeyAccount.getAddress(blockData.getGeneratorPublicKey()));
|
||||
json.put("generatorPublicKey", Base58.encode(blockData.getGeneratorPublicKey()));
|
||||
json.put("fee", blockData.getTotalFees().toPlainString());
|
||||
json.put("transactionsSignature", Base58.encode(blockData.getTransactionsSignature()));
|
||||
|
Loading…
Reference in New Issue
Block a user