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
|
// Constructors
|
||||||
|
|
||||||
public GenesisTransactionData(String recipient, BigDecimal amount, long timestamp, byte[] signature) {
|
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.recipient = recipient;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
|
@ -19,9 +19,7 @@ public class Account {
|
|||||||
|
|
||||||
public Account(Repository repository, String address) throws DataException {
|
public Account(Repository repository, String address) throws DataException {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.accountData = this.repository.getAccountRepository().getAccount(address);
|
this.accountData = new AccountData(address);
|
||||||
if (this.accountData == null)
|
|
||||||
this.accountData = new AccountData(address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAddress() {
|
public String getAddress() {
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
package qora.account;
|
package qora.account;
|
||||||
|
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.Repository;
|
||||||
|
|
||||||
public final class GenesisAccount extends PublicKeyAccount {
|
public final class GenesisAccount extends PublicKeyAccount {
|
||||||
|
|
||||||
public GenesisAccount() {
|
public static final byte[] PUBLIC_KEY = new byte[] { 1, 1, 1, 1, 1, 1, 1, 1 };
|
||||||
super(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;
|
package qora.account;
|
||||||
|
|
||||||
|
import data.account.AccountData;
|
||||||
import qora.crypto.Crypto;
|
import qora.crypto.Crypto;
|
||||||
import qora.crypto.Ed25519;
|
import qora.crypto.Ed25519;
|
||||||
|
import repository.Repository;
|
||||||
import utils.Pair;
|
import utils.Pair;
|
||||||
|
|
||||||
public class PrivateKeyAccount extends PublicKeyAccount {
|
public class PrivateKeyAccount extends PublicKeyAccount {
|
||||||
@ -15,11 +17,12 @@ public class PrivateKeyAccount extends PublicKeyAccount {
|
|||||||
* @param seed
|
* @param seed
|
||||||
* byte[32] used to create private/public key pair
|
* 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.seed = seed;
|
||||||
this.keyPair = Ed25519.createKeyPair(seed);
|
this.keyPair = Ed25519.createKeyPair(seed);
|
||||||
this.publicKey = keyPair.getB();
|
this.publicKey = keyPair.getB();
|
||||||
this.address = Crypto.toAddress(this.publicKey);
|
this.accountData = new AccountData(Crypto.toAddress(this.publicKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getSeed() {
|
public byte[] getSeed() {
|
||||||
|
@ -2,13 +2,15 @@ package qora.account;
|
|||||||
|
|
||||||
import qora.crypto.Crypto;
|
import qora.crypto.Crypto;
|
||||||
import qora.crypto.Ed25519;
|
import qora.crypto.Ed25519;
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.Repository;
|
||||||
|
|
||||||
public class PublicKeyAccount extends Account {
|
public class PublicKeyAccount extends Account {
|
||||||
|
|
||||||
protected byte[] publicKey;
|
protected byte[] publicKey;
|
||||||
|
|
||||||
public PublicKeyAccount(byte[] publicKey) {
|
public PublicKeyAccount(Repository repository, byte[] publicKey) throws DataException {
|
||||||
super(Crypto.toAddress(publicKey));
|
super(repository, Crypto.toAddress(publicKey));
|
||||||
|
|
||||||
this.publicKey = 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 {
|
public class Block {
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
private Repository repository;
|
protected Repository repository;
|
||||||
private BlockData blockData;
|
protected BlockData blockData;
|
||||||
private PublicKeyAccount generator;
|
protected PublicKeyAccount generator;
|
||||||
|
|
||||||
// Other properties
|
// Other properties
|
||||||
protected List<Transaction> transactions;
|
protected List<Transaction> transactions;
|
||||||
protected BigDecimal cachedNextGeneratingBalance;
|
protected BigDecimal cachedNextGeneratingBalance;
|
||||||
@ -76,10 +76,10 @@ public class Block {
|
|||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
public Block(Repository repository, BlockData blockData) {
|
public Block(Repository repository, BlockData blockData) throws DataException {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.blockData = blockData;
|
this.blockData = blockData;
|
||||||
this.generator = new PublicKeyAccount(blockData.getGeneratorPublicKey());
|
this.generator = new PublicKeyAccount(repository, blockData.getGeneratorPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters/setters
|
// Getters/setters
|
||||||
@ -88,6 +88,10 @@ public class Block {
|
|||||||
return this.blockData;
|
return this.blockData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PublicKeyAccount getGenerator() {
|
||||||
|
return this.generator;
|
||||||
|
}
|
||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,7 +146,7 @@ public class Block {
|
|||||||
// XXX: why can't we simply load using block height?
|
// XXX: why can't we simply load using block height?
|
||||||
BlockRepository blockRepo = this.repository.getBlockRepository();
|
BlockRepository blockRepo = this.repository.getBlockRepository();
|
||||||
BlockData firstBlock = this.blockData;
|
BlockData firstBlock = this.blockData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (int i = 1; firstBlock != null && i < BLOCK_RETARGET_INTERVAL; ++i)
|
for (int i = 1; firstBlock != null && i < BLOCK_RETARGET_INTERVAL; ++i)
|
||||||
firstBlock = blockRepo.fromSignature(firstBlock.getReference());
|
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");
|
throw new IllegalStateException("Block's transactions from repository do not match block's transaction count");
|
||||||
|
|
||||||
this.transactions = new ArrayList<Transaction>();
|
this.transactions = new ArrayList<Transaction>();
|
||||||
|
|
||||||
for (TransactionData transactionData : transactionsData)
|
for (TransactionData transactionData : transactionsData)
|
||||||
this.transactions.add(Transaction.fromData(transactionData));
|
this.transactions.add(Transaction.fromData(transactionData));
|
||||||
|
|
||||||
return this.transactions;
|
return this.transactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,7 +298,6 @@ public class Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isSignatureValid() {
|
public boolean isSignatureValid() {
|
||||||
try {
|
try {
|
||||||
// Check generator's signature first
|
// Check generator's signature first
|
||||||
@ -320,7 +323,7 @@ public class Block {
|
|||||||
*
|
*
|
||||||
* @return true if block is valid, false otherwise.
|
* @return true if block is valid, false otherwise.
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
* @throws DataException
|
* @throws DataException
|
||||||
*/
|
*/
|
||||||
public boolean isValid() throws SQLException, DataException {
|
public boolean isValid() throws SQLException, DataException {
|
||||||
// TODO
|
// TODO
|
||||||
@ -334,7 +337,7 @@ public class Block {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
Block parentBlock = new Block(this.repository, parentBlockData);
|
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
|
// 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())
|
if (this.blockData.getTimestamp() < parentBlockData.getTimestamp() || this.blockData.getTimestamp() - BLOCK_TIMESTAMP_MARGIN > NTP.getTime())
|
||||||
return false;
|
return false;
|
||||||
@ -410,7 +413,7 @@ public class Block {
|
|||||||
return true;
|
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)
|
// Process transactions (we'll link them to this block after saving the block itself)
|
||||||
List<Transaction> transactions = this.getTransactions();
|
List<Transaction> transactions = this.getTransactions();
|
||||||
for (Transaction transaction : transactions)
|
for (Transaction transaction : transactions)
|
||||||
@ -422,7 +425,7 @@ public class Block {
|
|||||||
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(blockFee));
|
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
|
// 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);
|
BlockData latestBlockData = this.repository.getBlockRepository().fromHeight(blockchainHeight);
|
||||||
if (latestBlockData != null)
|
if (latestBlockData != null)
|
||||||
this.blockData.setReference(latestBlockData.getSignature());
|
this.blockData.setReference(latestBlockData.getSignature());
|
||||||
@ -435,7 +438,8 @@ public class Block {
|
|||||||
Transaction transaction = transactions.get(sequence);
|
Transaction transaction = transactions.get(sequence);
|
||||||
|
|
||||||
// Link transaction to this block
|
// 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);
|
this.repository.getBlockRepository().save(blockTransactionData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package qora.block;
|
package qora.block;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import database.DB;
|
import data.block.BlockData;
|
||||||
import qora.assets.Asset;
|
import qora.assets.Asset;
|
||||||
|
import repository.BlockRepository;
|
||||||
|
import repository.DataException;
|
||||||
|
import repository.Repository;
|
||||||
|
import repository.RepositoryManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class representing the blockchain as a whole.
|
* Class representing the blockchain as a whole.
|
||||||
@ -35,69 +38,43 @@ public class BlockChain {
|
|||||||
*
|
*
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
public static void validate() throws SQLException {
|
public static void validate() throws DataException {
|
||||||
// Check first block is Genesis Block
|
// Check first block is Genesis Block
|
||||||
if (!isGenesisBlockValid())
|
if (!isGenesisBlockValid())
|
||||||
rebuildBlockchain();
|
rebuildBlockchain();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isGenesisBlockValid() throws SQLException {
|
private static boolean isGenesisBlockValid() throws DataException {
|
||||||
int blockchainHeight = getHeight();
|
BlockRepository blockRepository = RepositoryManager.getRepository().getBlockRepository();
|
||||||
|
|
||||||
|
int blockchainHeight = blockRepository.getBlockchainHeight();
|
||||||
if (blockchainHeight < 1)
|
if (blockchainHeight < 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Block block = Block.fromHeight(1);
|
BlockData blockData = blockRepository.fromHeight(1);
|
||||||
if (block == null)
|
if (blockData == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return GenesisBlock.isGenesisBlock(block);
|
return GenesisBlock.isGenesisBlock(blockData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void rebuildBlockchain() throws SQLException {
|
private static void rebuildBlockchain() throws DataException {
|
||||||
// (Re)build database
|
// (Re)build repository
|
||||||
DB.rebuild();
|
Repository repository = RepositoryManager.getRepository();
|
||||||
|
repository.rebuild();
|
||||||
|
|
||||||
// Add Genesis Block
|
// Add Genesis Block
|
||||||
GenesisBlock genesisBlock = GenesisBlock.getInstance();
|
GenesisBlock genesisBlock = new GenesisBlock(repository);
|
||||||
genesisBlock.process();
|
genesisBlock.process();
|
||||||
|
|
||||||
// Add QORA asset.
|
// Add QORA asset.
|
||||||
// NOTE: Asset's transaction reference is Genesis Block's generator signature which doesn't exist as a transaction!
|
// 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,
|
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();
|
qoraAsset.save();
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
repository.saveChanges();
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
package qora.block;
|
|
||||||
|
|
||||||
public class BlockTransaction {
|
|
||||||
|
|
||||||
}
|
|
@ -3,37 +3,34 @@ package qora.block;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
import com.google.common.primitives.Longs;
|
import com.google.common.primitives.Longs;
|
||||||
|
|
||||||
|
import data.block.BlockData;
|
||||||
|
import data.transaction.GenesisTransactionData;
|
||||||
|
import data.transaction.TransactionData;
|
||||||
import qora.account.GenesisAccount;
|
import qora.account.GenesisAccount;
|
||||||
import qora.crypto.Crypto;
|
import qora.crypto.Crypto;
|
||||||
import qora.transaction.GenesisTransaction;
|
import qora.transaction.Transaction;
|
||||||
import qora.transaction.TransactionHandler;
|
import repository.DataException;
|
||||||
|
import repository.Repository;
|
||||||
|
|
||||||
public class GenesisBlock extends Block {
|
public class GenesisBlock extends Block {
|
||||||
|
|
||||||
private static GenesisBlock instance;
|
|
||||||
|
|
||||||
private static final int GENESIS_BLOCK_VERSION = 1;
|
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 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 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 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_GENERATOR_SIGNATURE = calcSignature();
|
||||||
private static final byte[] GENESIS_TRANSACTIONS_SIGNATURE = calcSignature();
|
private static final byte[] GENESIS_TRANSACTIONS_SIGNATURE = calcSignature();
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
protected GenesisBlock() {
|
public GenesisBlock(Repository repository) throws DataException {
|
||||||
super(GENESIS_BLOCK_VERSION, GENESIS_REFERENCE, GENESIS_TIMESTAMP, GENESIS_GENERATING_BALANCE, GENESIS_GENERATOR, GENESIS_GENERATOR_SIGNATURE,
|
super(repository, new BlockData(GENESIS_BLOCK_VERSION, GENESIS_REFERENCE, 0, BigDecimal.ZERO.setScale(8), GENESIS_TRANSACTIONS_SIGNATURE, 1,
|
||||||
GENESIS_TRANSACTIONS_SIGNATURE, null, null, new ArrayList<TransactionHandler>());
|
GENESIS_TIMESTAMP, GENESIS_GENERATING_BALANCE, GENESIS_GENERATOR_PUBLIC_KEY, GENESIS_GENERATOR_SIGNATURE, null, null));
|
||||||
|
|
||||||
this.height = 1;
|
|
||||||
|
|
||||||
// Genesis transactions
|
// Genesis transactions
|
||||||
addGenesisTransaction("QUD9y7NZqTtNwvSAUfewd7zKUGoVivVnTW", "7032468.191");
|
addGenesisTransaction("QUD9y7NZqTtNwvSAUfewd7zKUGoVivVnTW", "7032468.191");
|
||||||
@ -172,74 +169,35 @@ public class GenesisBlock extends Block {
|
|||||||
addGenesisTransaction("QT79PhvBwE6vFzfZ4oh5wdKVsEazZuVJFy", "6360421.343");
|
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
|
// More information
|
||||||
|
|
||||||
public static boolean isGenesisBlock(Block block) {
|
public static boolean isGenesisBlock(BlockData blockData) {
|
||||||
if (block.height != 1)
|
if (blockData.getHeight() != 1)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Validate block signature
|
// Validate block signature
|
||||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, block.generatorSignature))
|
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, blockData.getGeneratorSignature()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Validate transactions signature
|
// Validate transactions signature
|
||||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, block.transactionsSignature))
|
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, blockData.getTransactionsSignature()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
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
|
// Processing
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addTransaction(TransactionHandler transaction) {
|
public boolean addTransaction(TransactionData transactionData) {
|
||||||
// The genesis block has a fixed set of transactions so always refuse.
|
// The genesis block has a fixed set of transactions so always refuse.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addGenesisTransaction(String recipient, String amount) {
|
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 {
|
try {
|
||||||
// Passing expected size to ByteArrayOutputStream avoids reallocation when adding more bytes than default 32.
|
// 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.
|
// 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
|
* 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()));
|
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.
|
// 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();
|
return bytes.toByteArray();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@ -317,25 +275,25 @@ public class GenesisBlock extends Block {
|
|||||||
@Override
|
@Override
|
||||||
public boolean isSignatureValid() {
|
public boolean isSignatureValid() {
|
||||||
// Validate block signature
|
// Validate block signature
|
||||||
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, this.generatorSignature))
|
if (!Arrays.equals(GENESIS_GENERATOR_SIGNATURE, this.getBlockData().getGeneratorSignature()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Validate transactions signature
|
// Validate transactions signature
|
||||||
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, this.transactionsSignature))
|
if (!Arrays.equals(GENESIS_TRANSACTIONS_SIGNATURE, this.getBlockData().getTransactionsSignature()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid() throws SQLException {
|
public boolean isValid() throws DataException {
|
||||||
// Check there is no other block in DB
|
// Check there is no other block in DB
|
||||||
if (BlockChain.getHeight() != 0)
|
if (this.repository.getBlockRepository().getBlockchainHeight() != 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Validate transactions
|
// Validate transactions
|
||||||
for (TransactionHandler transaction : this.getTransactions())
|
for (Transaction transaction : this.getTransactions())
|
||||||
if (transaction.isValid() != TransactionHandler.ValidationResult.OK)
|
if (transaction.isValid() != Transaction.ValidationResult.OK)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -12,6 +12,7 @@ import data.transaction.TransactionData;
|
|||||||
import qora.account.PrivateKeyAccount;
|
import qora.account.PrivateKeyAccount;
|
||||||
import qora.block.Block;
|
import qora.block.Block;
|
||||||
import qora.block.BlockChain;
|
import qora.block.BlockChain;
|
||||||
|
import repository.Repository;
|
||||||
import repository.RepositoryManager;
|
import repository.RepositoryManager;
|
||||||
import settings.Settings;
|
import settings.Settings;
|
||||||
import transform.TransformationException;
|
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 maxBytePerFee = BigDecimal.valueOf(Settings.getInstance().getMaxBytePerFee());
|
||||||
protected static final BigDecimal minFeePerByte = BigDecimal.ONE.divide(maxBytePerFee, MathContext.DECIMAL32);
|
protected static final BigDecimal minFeePerByte = BigDecimal.ONE.divide(maxBytePerFee, MathContext.DECIMAL32);
|
||||||
|
|
||||||
|
// Properties
|
||||||
protected TransactionData transactionData;
|
protected TransactionData transactionData;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
public static Transaction fromData(TransactionData transactionData) {
|
public static Transaction fromData(TransactionData transactionData) {
|
||||||
switch (transactionData.getType()) {
|
switch (transactionData.getType()) {
|
||||||
case GENESIS:
|
case GENESIS:
|
||||||
return new GenesisTransaction(transactionData);
|
return new GenesisTransaction(transactionData);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters / Setters
|
// Getters / Setters
|
||||||
|
|
||||||
public TransactionData getTransactionData() {
|
public TransactionData getTransactionData() {
|
||||||
return this.transactionData;
|
return this.transactionData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// More information
|
// More information
|
||||||
|
|
||||||
public long getDeadline() {
|
public long getDeadline() {
|
||||||
@ -108,17 +110,18 @@ public abstract class Transaction {
|
|||||||
|
|
||||||
public BigDecimal calcRecommendedFee() {
|
public BigDecimal calcRecommendedFee() {
|
||||||
try {
|
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
|
// security margin
|
||||||
recommendedFee = recommendedFee.add(new BigDecimal("0.0000001"));
|
recommendedFee = recommendedFee.add(new BigDecimal("0.0000001"));
|
||||||
|
|
||||||
if (recommendedFee.compareTo(MINIMUM_FEE) <= 0) {
|
if (recommendedFee.compareTo(MINIMUM_FEE) <= 0) {
|
||||||
recommendedFee = MINIMUM_FEE;
|
recommendedFee = MINIMUM_FEE;
|
||||||
} else {
|
} else {
|
||||||
recommendedFee = recommendedFee.setScale(0, BigDecimal.ROUND_UP);
|
recommendedFee = recommendedFee.setScale(0, BigDecimal.ROUND_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
return recommendedFee.setScale(8);
|
return recommendedFee.setScale(8);
|
||||||
} catch (TransformationException e) {
|
} catch (TransformationException e) {
|
||||||
throw new IllegalStateException("Unable to get transaction byte length?");
|
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)
|
* @return height, or 0 if not in blockchain (i.e. unconfirmed)
|
||||||
*/
|
*/
|
||||||
public int getHeight() {
|
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)
|
* @return confirmation count, or 0 if not in blockchain (i.e. unconfirmed)
|
||||||
*/
|
*/
|
||||||
public int getConfirmations() {
|
public int getConfirmations() {
|
||||||
int ourHeight = this.getHeight();
|
int ourHeight = getHeight();
|
||||||
if (ourHeight == 0)
|
if (ourHeight == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
int blockChainHeight = BlockChain.getHeight();
|
int blockChainHeight = BlockChain.getHeight();
|
||||||
if (blockChainHeight == 0)
|
if (blockChainHeight == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return blockChainHeight - ourHeight + 1;
|
return blockChainHeight - ourHeight + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,22 @@ public interface BlockRepository {
|
|||||||
|
|
||||||
public BlockData fromHeight(int height) throws DataException;
|
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 List<TransactionData> getTransactionsFromSignature(byte[] signature) throws DataException;
|
||||||
|
|
||||||
public void save(BlockData blockData) throws DataException;
|
public void save(BlockData blockData) throws DataException;
|
||||||
|
@ -14,4 +14,6 @@ public interface Repository {
|
|||||||
|
|
||||||
public void close() throws DataException;
|
public void close() throws DataException;
|
||||||
|
|
||||||
|
public void rebuild() throws DataException;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,31 @@ public class HSQLDBBlockRepository implements BlockRepository {
|
|||||||
this.repository = repository;
|
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 {
|
public BlockData fromSignature(byte[] signature) throws DataException {
|
||||||
try {
|
try {
|
||||||
ResultSet rs = this.repository.checkedExecute("SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE signature = ?", signature);
|
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 {
|
public int getHeightFromSignature(byte[] signature) throws DataException {
|
||||||
if (rs == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int version = rs.getInt(1);
|
ResultSet rs = this.repository.checkedExecute("SELECT height FROM Blocks WHERE signature = ?", signature);
|
||||||
byte[] reference = this.repository.getResultSetBytes(rs.getBinaryStream(2));
|
if (rs == null)
|
||||||
int transactionCount = rs.getInt(3);
|
return 0;
|
||||||
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,
|
return rs.getInt(1);
|
||||||
generatorPublicKey, generatorSignature, atBytes, atFees);
|
|
||||||
} catch (SQLException e) {
|
} 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[].
|
* Convert InputStream, from ResultSet.getBinaryStream(), into byte[].
|
||||||
*
|
*
|
||||||
|
@ -210,7 +210,7 @@ public class BlockTransformer extends Transformer {
|
|||||||
json.put("version", blockData.getVersion());
|
json.put("version", blockData.getVersion());
|
||||||
json.put("timestamp", blockData.getTimestamp());
|
json.put("timestamp", blockData.getTimestamp());
|
||||||
json.put("generatingBalance", blockData.getGeneratingBalance());
|
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("generatorPublicKey", Base58.encode(blockData.getGeneratorPublicKey()));
|
||||||
json.put("fee", blockData.getTotalFees().toPlainString());
|
json.put("fee", blockData.getTotalFees().toPlainString());
|
||||||
json.put("transactionsSignature", Base58.encode(blockData.getTransactionsSignature()));
|
json.put("transactionsSignature", Base58.encode(blockData.getTransactionsSignature()));
|
||||||
|
Loading…
Reference in New Issue
Block a user