Proxy forging improvements + account flags fixes

Proxy forging recipient no longer needs a public key on the blockchain
at the point PROXY_FORGING transaction is submitted.

Proxy forging recipient is given a last-reference, if they don't have one,
when they receive their first block rewards.

Split block fees in proxy forging scenario, using same share proportion.

100% proxy sharing is now allowed.

Fixed account flags processing for accounts in genesis block.
This commit is contained in:
catbref 2019-05-15 10:40:13 +01:00
parent 3ffcf50d7c
commit 7a318c9fc7
4 changed files with 81 additions and 15 deletions

View File

@ -1050,10 +1050,8 @@ public class Block {
for (Transaction transaction : transactions) for (Transaction transaction : transactions)
transaction.process(); transaction.process();
// If fees are non-zero then add fees to generator's balance // Give transaction fees to generator/proxy
BigDecimal blockFee = this.blockData.getTotalFees(); rewardTransactionFees();
if (blockFee.compareTo(BigDecimal.ZERO) > 0)
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(blockFee));
// Process AT fees and save AT states into repository // Process AT fees and save AT states into repository
ATRepository atRepository = this.repository.getATRepository(); ATRepository atRepository = this.repository.getATRepository();
@ -1102,7 +1100,7 @@ public class Block {
// Is generator public key actually a proxy forge key? // Is generator public key actually a proxy forge key?
ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.blockData.getGeneratorPublicKey()); ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.blockData.getGeneratorPublicKey());
if (proxyForgerData != null) { if (proxyForgerData != null) {
// Split reward to forger and recipient; // Split reward between forger and recipient
Account recipient = new Account(this.repository, proxyForgerData.getRecipient()); Account recipient = new Account(this.repository, proxyForgerData.getRecipient());
BigDecimal recipientShare = reward.multiply(proxyForgerData.getShare().movePointLeft(2)).setScale(8, RoundingMode.DOWN); BigDecimal recipientShare = reward.multiply(proxyForgerData.getShare().movePointLeft(2)).setScale(8, RoundingMode.DOWN);
recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).add(recipientShare)); recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).add(recipientShare));
@ -1117,6 +1115,31 @@ public class Block {
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(reward)); this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(reward));
} }
protected void rewardTransactionFees() throws DataException {
BigDecimal blockFees = this.blockData.getTotalFees();
// No transaction fees?
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
return;
// Is generator public key actually a proxy forge key?
ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.blockData.getGeneratorPublicKey());
if (proxyForgerData != null) {
// Split fees between forger and recipient
Account recipient = new Account(this.repository, proxyForgerData.getRecipient());
BigDecimal recipientShare = blockFees.multiply(proxyForgerData.getShare().movePointLeft(2)).setScale(8, RoundingMode.DOWN);
recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).add(recipientShare));
Account forger = new PublicKeyAccount(this.repository, proxyForgerData.getForgerPublicKey());
BigDecimal forgerShare = blockFees.subtract(recipientShare);
forger.setConfirmedBalance(Asset.QORA, forger.getConfirmedBalance(Asset.QORA).add(forgerShare));
return;
}
// Give transaction fees to generator
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).add(blockFees));
}
/** /**
* Removes block from blockchain undoing transactions and adding them to unconfirmed pile. * Removes block from blockchain undoing transactions and adding them to unconfirmed pile.
* *
@ -1145,10 +1168,8 @@ public class Block {
// Block rewards removed after transactions undone // Block rewards removed after transactions undone
orphanBlockRewards(); orphanBlockRewards();
// If fees are non-zero then remove fees from generator's balance // Deduct any transaction fees from generator/proxy
BigDecimal blockFee = this.blockData.getTotalFees(); deductTransactionFees();
if (blockFee.compareTo(BigDecimal.ZERO) > 0)
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).subtract(blockFee));
// Return AT fees and delete AT states from repository // Return AT fees and delete AT states from repository
ATRepository atRepository = this.repository.getATRepository(); ATRepository atRepository = this.repository.getATRepository();
@ -1175,7 +1196,7 @@ public class Block {
// Is generator public key actually a proxy forge key? // Is generator public key actually a proxy forge key?
ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.blockData.getGeneratorPublicKey()); ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.blockData.getGeneratorPublicKey());
if (proxyForgerData != null) { if (proxyForgerData != null) {
// Split reward from forger and recipient; // Split reward between forger and recipient
Account recipient = new Account(this.repository, proxyForgerData.getRecipient()); Account recipient = new Account(this.repository, proxyForgerData.getRecipient());
BigDecimal recipientShare = reward.multiply(proxyForgerData.getShare().movePointLeft(2)).setScale(8, RoundingMode.DOWN); BigDecimal recipientShare = reward.multiply(proxyForgerData.getShare().movePointLeft(2)).setScale(8, RoundingMode.DOWN);
recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).subtract(recipientShare)); recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).subtract(recipientShare));
@ -1190,6 +1211,31 @@ public class Block {
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).subtract(reward)); this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).subtract(reward));
} }
protected void deductTransactionFees() throws DataException {
BigDecimal blockFees = this.blockData.getTotalFees();
// No transaction fees?
if (blockFees.compareTo(BigDecimal.ZERO) <= 0)
return;
// Is generator public key actually a proxy forge key?
ProxyForgerData proxyForgerData = this.repository.getAccountRepository().getProxyForgeData(this.blockData.getGeneratorPublicKey());
if (proxyForgerData != null) {
// Split fees between forger and recipient
Account recipient = new Account(this.repository, proxyForgerData.getRecipient());
BigDecimal recipientShare = blockFees.multiply(proxyForgerData.getShare().movePointLeft(2)).setScale(8, RoundingMode.DOWN);
recipient.setConfirmedBalance(Asset.QORA, recipient.getConfirmedBalance(Asset.QORA).subtract(recipientShare));
Account forger = new PublicKeyAccount(this.repository, proxyForgerData.getForgerPublicKey());
BigDecimal forgerShare = blockFees.subtract(recipientShare);
forger.setConfirmedBalance(Asset.QORA, forger.getConfirmedBalance(Asset.QORA).subtract(forgerShare));
return;
}
// Deduct transaction fees to generator
this.generator.setConfirmedBalance(Asset.QORA, this.generator.getConfirmedBalance(Asset.QORA).subtract(blockFees));
}
protected BigDecimal getRewardAtHeight(int ourHeight) { protected BigDecimal getRewardAtHeight(int ourHeight) {
List<RewardByHeight> rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight(); List<RewardByHeight> rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight();

View File

@ -78,7 +78,6 @@ public class HSQLDBAccountRepository implements AccountRepository {
if (resultSet == null) if (resultSet == null)
return null; return null;
// Column is NOT NULL so this should never implicitly convert to 0
return resultSet.getInt(1); return resultSet.getInt(1);
} catch (SQLException e) { } catch (SQLException e) {
throw new DataException("Unable to fetch account's flags from repository", e); throw new DataException("Unable to fetch account's flags from repository", e);

View File

@ -91,13 +91,17 @@ public class AccountFlagsTransaction extends Transaction {
@Override @Override
public void process() throws DataException { public void process() throws DataException {
Account target = getTarget(); Account target = getTarget();
int previousFlags = target.getFlags(); Integer previousFlags = target.getFlags();
accountFlagsTransactionData.setPreviousFlags(previousFlags); accountFlagsTransactionData.setPreviousFlags(previousFlags);
// Save this transaction with target account's previous flags value // Save this transaction with target account's previous flags value
this.repository.getTransactionRepository().save(accountFlagsTransactionData); this.repository.getTransactionRepository().save(accountFlagsTransactionData);
// If account doesn't have entry in database yet (e.g. genesis block) then flags are zero
if (previousFlags == null)
previousFlags = 0;
// Set account's new flags // Set account's new flags
int newFlags = previousFlags & accountFlagsTransactionData.getAndMask() int newFlags = previousFlags & accountFlagsTransactionData.getAndMask()
| accountFlagsTransactionData.getOrMask() ^ accountFlagsTransactionData.getXorMask(); | accountFlagsTransactionData.getOrMask() ^ accountFlagsTransactionData.getXorMask();
@ -117,7 +121,13 @@ public class AccountFlagsTransaction extends Transaction {
// Revert // Revert
Account target = getTarget(); Account target = getTarget();
target.setFlags(accountFlagsTransactionData.getPreviousFlags()); Integer previousFlags = accountFlagsTransactionData.getPreviousFlags();
// If previousFlags are null then account didn't exist before this transaction
if (previousFlags == null)
this.repository.getAccountRepository().delete(target.getAddress());
else
target.setFlags(previousFlags);
// Delete this transaction itself // Delete this transaction itself
this.repository.getTransactionRepository().delete(accountFlagsTransactionData); this.repository.getTransactionRepository().delete(accountFlagsTransactionData);

View File

@ -10,7 +10,6 @@ import org.qora.account.Forging;
import org.qora.account.PublicKeyAccount; import org.qora.account.PublicKeyAccount;
import org.qora.asset.Asset; import org.qora.asset.Asset;
import org.qora.crypto.Crypto; import org.qora.crypto.Crypto;
import org.qora.data.account.AccountData;
import org.qora.data.account.ProxyForgerData; import org.qora.data.account.ProxyForgerData;
import org.qora.data.transaction.ProxyForgingTransactionData; import org.qora.data.transaction.ProxyForgingTransactionData;
import org.qora.data.transaction.TransactionData; import org.qora.data.transaction.TransactionData;
@ -80,7 +79,7 @@ public class ProxyForgingTransaction extends Transaction {
public ValidationResult isValid() throws DataException { public ValidationResult isValid() throws DataException {
// Check reward share given to recipient // Check reward share given to recipient
if (this.proxyForgingTransactionData.getShare().compareTo(BigDecimal.ZERO) <= 0 if (this.proxyForgingTransactionData.getShare().compareTo(BigDecimal.ZERO) <= 0
|| this.proxyForgingTransactionData.getShare().compareTo(MAX_SHARE) >= 0) || this.proxyForgingTransactionData.getShare().compareTo(MAX_SHARE) > 0)
return ValidationResult.INVALID_FORGE_SHARE; return ValidationResult.INVALID_FORGE_SHARE;
PublicKeyAccount creator = getCreator(); PublicKeyAccount creator = getCreator();
@ -97,10 +96,12 @@ public class ProxyForgingTransaction extends Transaction {
if (!Crypto.isValidAddress(recipient.getAddress())) if (!Crypto.isValidAddress(recipient.getAddress()))
return ValidationResult.INVALID_ADDRESS; return ValidationResult.INVALID_ADDRESS;
/* Not needed?
// Check recipient has known public key // Check recipient has known public key
AccountData recipientData = this.repository.getAccountRepository().getAccount(recipient.getAddress()); AccountData recipientData = this.repository.getAccountRepository().getAccount(recipient.getAddress());
if (recipientData == null || recipientData.getPublicKey() == null) if (recipientData == null || recipientData.getPublicKey() == null)
return ValidationResult.PUBLIC_KEY_UNKNOWN; return ValidationResult.PUBLIC_KEY_UNKNOWN;
*/
// Check fee is positive // Check fee is positive
if (proxyForgingTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0) if (proxyForgingTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
@ -140,6 +141,11 @@ public class ProxyForgingTransaction extends Transaction {
// Update forger's reference // Update forger's reference
forger.setLastReference(proxyForgingTransactionData.getSignature()); forger.setLastReference(proxyForgingTransactionData.getSignature());
// If proxy recipient has no last-reference then use this transaction's signature as last-reference so they can spend their block rewards
Account recipient = new Account(this.repository, proxyForgingTransactionData.getRecipient());
if (recipient.getLastReference() == null)
recipient.setLastReference(proxyForgingTransactionData.getSignature());
} }
@Override @Override
@ -166,6 +172,11 @@ public class ProxyForgingTransaction extends Transaction {
// Update forger's reference // Update forger's reference
forger.setLastReference(proxyForgingTransactionData.getReference()); forger.setLastReference(proxyForgingTransactionData.getReference());
// If recipient didn't have a last-reference prior to this transaction then remove it
Account recipient = new Account(this.repository, proxyForgingTransactionData.getRecipient());
if (Arrays.equals(recipient.getLastReference(), proxyForgingTransactionData.getSignature()))
recipient.setLastReference(null);
} }
} }