package qora.account; import java.math.BigDecimal; import data.account.AccountBalanceData; import data.account.AccountData; import data.block.BlockData; import qora.assets.Asset; import qora.block.Block; import qora.block.BlockChain; import qora.transaction.Transaction; import repository.BlockRepository; import repository.DataException; import repository.Repository; public class Account { public static final int ADDRESS_LENGTH = 25; protected Repository repository; protected AccountData accountData; protected Account() { } public Account(Repository repository, String address) throws DataException { this.repository = repository; this.accountData = new AccountData(address); } public String getAddress() { return this.accountData.getAddress(); } // More information /** * Calculate current generating balance for this account. *

* This is the current confirmed balance minus amounts received in the last BlockChain.BLOCK_RETARGET_INTERVAL blocks. * * @throws DataException */ public BigDecimal getGeneratingBalance() throws DataException { BigDecimal balance = this.getConfirmedBalance(Asset.QORA); BlockRepository blockRepository = this.repository.getBlockRepository(); BlockData blockData = blockRepository.getLastBlock(); for (int i = 1; i < BlockChain.BLOCK_RETARGET_INTERVAL && blockData != null && blockData.getHeight() > 1; ++i) { Block block = new Block(this.repository, blockData); for (Transaction transaction : block.getTransactions()) { if (transaction.isInvolved(this)) { final BigDecimal amount = transaction.getAmount(this); // Subtract positive amounts only if (amount.compareTo(BigDecimal.ZERO) > 0) balance = balance.subtract(amount); } } // TODO - CIYAM AT support needed /* * LinkedHashMap, AT_Transaction> atTxs = db.getATTransactionMap().getATTransactions(block.getHeight(db)); * Iterator iter = atTxs.values().iterator(); while (iter.hasNext()) { AT_Transaction key = iter.next(); * * if (key.getRecipient().equals(this.getAddress())) balance = balance.subtract(BigDecimal.valueOf(key.getAmount(), 8)); } */ blockData = block.getParent(); } // Do not go below 0 // XXX: How would this even be possible? balance = balance.max(BigDecimal.ZERO); return balance; } // Balance manipulations - assetId is 0 for QORA public BigDecimal getBalance(long assetId, int confirmations) throws DataException { // Simple case: we only need balance with 1 confirmation if (confirmations == 1) return this.getConfirmedBalance(assetId); /* * For a balance with more confirmations work back from last block, undoing transactions involving this account, until we have processed required number * of blocks. */ BlockRepository blockRepository = this.repository.getBlockRepository(); BigDecimal balance = this.getConfirmedBalance(assetId); BlockData blockData = blockRepository.getLastBlock(); // Note: "blockData.getHeight() > 1" to make sure we don't examine genesis block for (int i = 1; i < confirmations && blockData != null && blockData.getHeight() > 1; ++i) { Block block = new Block(this.repository, blockData); for (Transaction transaction : block.getTransactions()) if (transaction.isInvolved(this)) balance = balance.subtract(transaction.getAmount(this)); // TODO - CIYAM AT support /* * // Also check AT transactions for amounts received to this account LinkedHashMap, AT_Transaction> atTxs = * db.getATTransactionMap().getATTransactions(block.getHeight(db)); Iterator iter = atTxs.values().iterator(); while * (iter.hasNext()) { AT_Transaction key = iter.next(); * * if (key.getRecipient().equals(this.getAddress())) balance = balance.subtract(BigDecimal.valueOf(key.getAmount(), 8)); } */ blockData = block.getParent(); } // Return balance return balance; } public BigDecimal getConfirmedBalance(long assetId) throws DataException { AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.accountData.getAddress(), assetId); if (accountBalanceData == null) return BigDecimal.ZERO.setScale(8); return accountBalanceData.getBalance(); } public void setConfirmedBalance(long assetId, BigDecimal balance) throws DataException { AccountBalanceData accountBalanceData = new AccountBalanceData(this.accountData.getAddress(), assetId, balance); this.repository.getAccountRepository().save(accountBalanceData); } public void deleteBalance(long assetId) throws DataException { this.repository.getAccountRepository().delete(this.accountData.getAddress(), assetId); } // Reference manipulations /** * Fetch last reference for account. * * @return byte[] reference, or null if no reference or account not found. * @throws DataException */ public byte[] getLastReference() throws DataException { AccountData accountData = this.repository.getAccountRepository().getAccount(this.accountData.getAddress()); if (accountData == null) return null; return accountData.getReference(); } /** * Set last reference for account. * * @param reference * -- null allowed * @throws DataException */ public void setLastReference(byte[] reference) throws DataException { accountData.setReference(reference); this.repository.getAccountRepository().save(accountData); } }