diff --git a/src/data/transaction/GenesisTransactionData.java b/src/data/transaction/GenesisTransactionData.java index ce813bf3..6bbca88e 100644 --- a/src/data/transaction/GenesisTransactionData.java +++ b/src/data/transaction/GenesisTransactionData.java @@ -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; diff --git a/src/qora/account/Account.java b/src/qora/account/Account.java index 040f2c7c..3153d2cf 100644 --- a/src/qora/account/Account.java +++ b/src/qora/account/Account.java @@ -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() { diff --git a/src/qora/account/GenesisAccount.java b/src/qora/account/GenesisAccount.java index 170983d6..a989ec4a 100644 --- a/src/qora/account/GenesisAccount.java +++ b/src/qora/account/GenesisAccount.java @@ -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); } } diff --git a/src/qora/account/PrivateKeyAccount.java b/src/qora/account/PrivateKeyAccount.java index 356a2b02..317f6f47 100644 --- a/src/qora/account/PrivateKeyAccount.java +++ b/src/qora/account/PrivateKeyAccount.java @@ -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() { diff --git a/src/qora/account/PublicKeyAccount.java b/src/qora/account/PublicKeyAccount.java index 121cfeb2..2c90ac78 100644 --- a/src/qora/account/PublicKeyAccount.java +++ b/src/qora/account/PublicKeyAccount.java @@ -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); + } + } diff --git a/src/qora/block/Block.java b/src/qora/block/Block.java index 4c5283fd..76b6c77f 100644 --- a/src/qora/block/Block.java +++ b/src/qora/block/Block.java @@ -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 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(); - + 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 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); } } diff --git a/src/qora/block/BlockChain.java b/src/qora/block/BlockChain.java index 9850fb59..8577a2d4 100644 --- a/src/qora/block/BlockChain.java +++ b/src/qora/block/BlockChain.java @@ -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(); } /** diff --git a/src/qora/block/BlockTransaction.java b/src/qora/block/BlockTransaction.java deleted file mode 100644 index 99ef0915..00000000 --- a/src/qora/block/BlockTransaction.java +++ /dev/null @@ -1,5 +0,0 @@ -package qora.block; - -public class BlockTransaction { - -} diff --git a/src/qora/block/GenesisBlock.java b/src/qora/block/GenesisBlock.java index c79c31e2..675681d6 100644 --- a/src/qora/block/GenesisBlock.java +++ b/src/qora/block/GenesisBlock.java @@ -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()); - - 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! - *

- * 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; diff --git a/src/qora/transaction/Transaction.java b/src/qora/transaction/Transaction.java index 0bb31897..769a0b7f 100644 --- a/src/qora/transaction/Transaction.java +++ b/src/qora/transaction/Transaction.java @@ -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; } diff --git a/src/repository/BlockRepository.java b/src/repository/BlockRepository.java index e206bc6a..6d6213c1 100644 --- a/src/repository/BlockRepository.java +++ b/src/repository/BlockRepository.java @@ -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 getTransactionsFromSignature(byte[] signature) throws DataException; public void save(BlockData blockData) throws DataException; diff --git a/src/repository/Repository.java b/src/repository/Repository.java index 8598b272..f607f6e0 100644 --- a/src/repository/Repository.java +++ b/src/repository/Repository.java @@ -14,4 +14,6 @@ public interface Repository { public void close() throws DataException; + public void rebuild() throws DataException; + } diff --git a/src/repository/hsqldb/HSQLDBBlockRepository.java b/src/repository/hsqldb/HSQLDBBlockRepository.java index fe9b7f2f..f60c8408 100644 --- a/src/repository/hsqldb/HSQLDBBlockRepository.java +++ b/src/repository/hsqldb/HSQLDBBlockRepository.java @@ -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); } } diff --git a/src/repository/hsqldb/HSQLDBRepository.java b/src/repository/hsqldb/HSQLDBRepository.java index 04386265..7496d393 100644 --- a/src/repository/hsqldb/HSQLDBRepository.java +++ b/src/repository/hsqldb/HSQLDBRepository.java @@ -69,6 +69,10 @@ public class HSQLDBRepository implements Repository { } } + @Override + public void rebuild() throws DataException { + } + /** * Convert InputStream, from ResultSet.getBinaryStream(), into byte[]. * diff --git a/src/transform/block/BlockTransformer.java b/src/transform/block/BlockTransformer.java index 424980c4..61cf5ef2 100644 --- a/src/transform/block/BlockTransformer.java +++ b/src/transform/block/BlockTransformer.java @@ -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()));