forked from Qortal/qortal
Initial support for account flags + tx (genesis account use only atm)
This commit is contained in:
parent
85acc4d9df
commit
2dc1720af8
@ -226,4 +226,16 @@ public class Account {
|
|||||||
LOGGER.trace(String.format("Account %s defaultGroupId now %d", accountData.getAddress(), defaultGroupId));
|
LOGGER.trace(String.format("Account %s defaultGroupId now %d", accountData.getAddress(), defaultGroupId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Account flags
|
||||||
|
|
||||||
|
public Integer getFlags() throws DataException {
|
||||||
|
return this.repository.getAccountRepository().getFlags(this.address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlags(int flags) throws DataException {
|
||||||
|
AccountData accountData = this.buildAccountData();
|
||||||
|
accountData.setFlags(flags);
|
||||||
|
this.repository.getAccountRepository().setFlags(accountData);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ public class AccountData {
|
|||||||
protected byte[] reference;
|
protected byte[] reference;
|
||||||
protected byte[] publicKey;
|
protected byte[] publicKey;
|
||||||
protected int defaultGroupId;
|
protected int defaultGroupId;
|
||||||
|
protected int flags;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -21,15 +22,16 @@ public class AccountData {
|
|||||||
protected AccountData() {
|
protected AccountData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountData(String address, byte[] reference, byte[] publicKey, int defaultGroupId) {
|
public AccountData(String address, byte[] reference, byte[] publicKey, int defaultGroupId, int flags) {
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.reference = reference;
|
this.reference = reference;
|
||||||
this.publicKey = publicKey;
|
this.publicKey = publicKey;
|
||||||
this.defaultGroupId = defaultGroupId;
|
this.defaultGroupId = defaultGroupId;
|
||||||
|
this.flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountData(String address) {
|
public AccountData(String address) {
|
||||||
this(address, null, null, Group.NO_GROUP);
|
this(address, null, null, Group.NO_GROUP, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Getters/Setters
|
// Getters/Setters
|
||||||
@ -62,6 +64,14 @@ public class AccountData {
|
|||||||
this.defaultGroupId = defaultGroupId;
|
this.defaultGroupId = defaultGroupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getFlags() {
|
||||||
|
return this.flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFlags(int flags) {
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
// Comparison
|
// Comparison
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
package org.qora.data.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
import javax.xml.bind.Unmarshaller;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessType;
|
||||||
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
|
||||||
|
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorValue;
|
||||||
|
import org.qora.account.GenesisAccount;
|
||||||
|
import org.qora.block.GenesisBlock;
|
||||||
|
import org.qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
|
||||||
|
// All properties to be converted to JSON via JAXB
|
||||||
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@Schema(allOf = {TransactionData.class})
|
||||||
|
//JAXB: use this subclass if XmlDiscriminatorNode matches XmlDiscriminatorValue below:
|
||||||
|
@XmlDiscriminatorValue("ACCOUNT_FLAGS")
|
||||||
|
public class AccountFlagsTransactionData extends TransactionData {
|
||||||
|
|
||||||
|
private String target;
|
||||||
|
private int andMask;
|
||||||
|
private int orMask;
|
||||||
|
private int xorMask;
|
||||||
|
private Integer previousFlags;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
// For JAXB
|
||||||
|
protected AccountFlagsTransactionData() {
|
||||||
|
super(TransactionType.ACCOUNT_FLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterUnmarshal(Unmarshaller u, Object parent) {
|
||||||
|
/*
|
||||||
|
* If we're being constructed as part of the genesis block info inside blockchain config
|
||||||
|
* and no specific creator's public key is supplied
|
||||||
|
* then use genesis account's public key.
|
||||||
|
*/
|
||||||
|
if (parent instanceof GenesisBlock.GenesisInfo && this.creatorPublicKey == null)
|
||||||
|
this.creatorPublicKey = GenesisAccount.PUBLIC_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountFlagsTransactionData(long timestamp, int groupId, byte[] reference, byte[] creatorPublicKey, String target, int andMask, int orMask,
|
||||||
|
int xorMask, Integer previousFlags, BigDecimal fee, byte[] signature) {
|
||||||
|
super(TransactionType.ACCOUNT_FLAGS, timestamp, groupId, reference, creatorPublicKey, fee, signature);
|
||||||
|
|
||||||
|
this.target = target;
|
||||||
|
this.andMask = andMask;
|
||||||
|
this.orMask = orMask;
|
||||||
|
this.xorMask = xorMask;
|
||||||
|
this.previousFlags = previousFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typically used in deserialization context
|
||||||
|
public AccountFlagsTransactionData(long timestamp, int groupId, byte[] reference, byte[] creatorPublicKey, String target, int andMask, int orMask,
|
||||||
|
int xorMask, BigDecimal fee, byte[] signature) {
|
||||||
|
this(timestamp, groupId, reference, creatorPublicKey, target, andMask, orMask, xorMask, null, fee, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters / setters
|
||||||
|
|
||||||
|
public String getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getAndMask() {
|
||||||
|
return this.andMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getOrMask() {
|
||||||
|
return this.orMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getXorMask() {
|
||||||
|
return this.xorMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getPreviousFlags() {
|
||||||
|
return this.previousFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPreviousFlags(Integer previousFlags) {
|
||||||
|
this.previousFlags = previousFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-expose to JAXB
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@XmlElement
|
||||||
|
public byte[] getCreatorPublicKey() {
|
||||||
|
return super.getCreatorPublicKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@XmlElement
|
||||||
|
public void setCreatorPublicKey(byte[] creatorPublicKey) {
|
||||||
|
super.setCreatorPublicKey(creatorPublicKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -37,7 +37,8 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
|||||||
GroupKickTransactionData.class, GroupInviteTransactionData.class,
|
GroupKickTransactionData.class, GroupInviteTransactionData.class,
|
||||||
JoinGroupTransactionData.class, LeaveGroupTransactionData.class,
|
JoinGroupTransactionData.class, LeaveGroupTransactionData.class,
|
||||||
GroupApprovalTransactionData.class, SetGroupTransactionData.class,
|
GroupApprovalTransactionData.class, SetGroupTransactionData.class,
|
||||||
UpdateAssetTransactionData.class
|
UpdateAssetTransactionData.class,
|
||||||
|
AccountFlagsTransactionData.class
|
||||||
})
|
})
|
||||||
//All properties to be converted to JSON via JAXB
|
//All properties to be converted to JSON via JAXB
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
|
@ -18,6 +18,9 @@ public interface AccountRepository {
|
|||||||
/** Returns account's default groupID or null if account not found. */
|
/** Returns account's default groupID or null if account not found. */
|
||||||
public Integer getDefaultGroupId(String address) throws DataException;
|
public Integer getDefaultGroupId(String address) throws DataException;
|
||||||
|
|
||||||
|
/** Returns account's flags or null if account not found. */
|
||||||
|
public Integer getFlags(String address) throws DataException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures at least minimal account info in repository.
|
* Ensures at least minimal account info in repository.
|
||||||
* <p>
|
* <p>
|
||||||
@ -39,6 +42,13 @@ public interface AccountRepository {
|
|||||||
*/
|
*/
|
||||||
public void setDefaultGroupId(AccountData accountData) throws DataException;
|
public void setDefaultGroupId(AccountData accountData) throws DataException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves account's flags, and public key if present, in repository.
|
||||||
|
* <p>
|
||||||
|
* Note: ignores other fields like last reference, default groupID.
|
||||||
|
*/
|
||||||
|
public void setFlags(AccountData accountData) throws DataException;
|
||||||
|
|
||||||
public void delete(String address) throws DataException;
|
public void delete(String address) throws DataException;
|
||||||
|
|
||||||
// Account balances
|
// Account balances
|
||||||
|
@ -26,15 +26,16 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccountData getAccount(String address) throws DataException {
|
public AccountData getAccount(String address) throws DataException {
|
||||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT reference, public_key, default_group_id FROM Accounts WHERE account = ?", address)) {
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT reference, public_key, default_group_id, flags FROM Accounts WHERE account = ?", address)) {
|
||||||
if (resultSet == null)
|
if (resultSet == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
byte[] reference = resultSet.getBytes(1);
|
byte[] reference = resultSet.getBytes(1);
|
||||||
byte[] publicKey = resultSet.getBytes(2);
|
byte[] publicKey = resultSet.getBytes(2);
|
||||||
int defaultGroupId = resultSet.getInt(3);
|
int defaultGroupId = resultSet.getInt(3);
|
||||||
|
int flags = resultSet.getInt(4);
|
||||||
|
|
||||||
return new AccountData(address, reference, publicKey, defaultGroupId);
|
return new AccountData(address, reference, publicKey, defaultGroupId, flags);
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new DataException("Unable to fetch account info from repository", e);
|
throw new DataException("Unable to fetch account info from repository", e);
|
||||||
}
|
}
|
||||||
@ -65,6 +66,19 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer getFlags(String address) throws DataException {
|
||||||
|
try (ResultSet resultSet = this.repository.checkedExecute("SELECT flags FROM Accounts WHERE account = ?", address)) {
|
||||||
|
if (resultSet == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Column is NOT NULL so this should never implicitly convert to 0
|
||||||
|
return resultSet.getInt(1);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch account's flags from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void ensureAccount(AccountData accountData) throws DataException {
|
public void ensureAccount(AccountData accountData) throws DataException {
|
||||||
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
|
||||||
@ -116,6 +130,23 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFlags(AccountData accountData) throws DataException {
|
||||||
|
HSQLDBSaver saveHelper = new HSQLDBSaver("Accounts");
|
||||||
|
|
||||||
|
saveHelper.bind("account", accountData.getAddress()).bind("flags", accountData.getFlags());
|
||||||
|
|
||||||
|
byte[] publicKey = accountData.getPublicKey();
|
||||||
|
if (publicKey != null)
|
||||||
|
saveHelper.bind("public_key", publicKey);
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveHelper.execute(this.repository);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to save account's flags into repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void delete(String address) throws DataException {
|
public void delete(String address) throws DataException {
|
||||||
// NOTE: Account balances are deleted automatically by the database thanks to "ON DELETE CASCADE" in AccountBalances' FOREIGN KEY
|
// NOTE: Account balances are deleted automatically by the database thanks to "ON DELETE CASCADE" in AccountBalances' FOREIGN KEY
|
||||||
|
@ -690,6 +690,14 @@ public class HSQLDBDatabaseUpdates {
|
|||||||
stmt.execute("ALTER TABLE AssetTrades ADD initiator_saving QoraAmount NOT NULL DEFAULT 0");
|
stmt.execute("ALTER TABLE AssetTrades ADD initiator_saving QoraAmount NOT NULL DEFAULT 0");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 44:
|
||||||
|
// Account flags
|
||||||
|
stmt.execute("ALTER TABLE Accounts ADD COLUMN flags INT NOT NULL DEFAULT 0");
|
||||||
|
// Corresponding transaction to set/clear flags
|
||||||
|
stmt.execute("CREATE TABLE AccountFlagsTransactions (signature Signature, creator QoraPublicKey NOT NULL, target QoraAddress NOT NULL, and_mask INT NOT NULL, or_mask INT NOT NULL, xor_mask INT NOT NULL, "
|
||||||
|
+ "previous_flags INT, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
return false;
|
return false;
|
||||||
|
@ -0,0 +1,59 @@
|
|||||||
|
package org.qora.repository.hsqldb.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
|
import org.qora.data.transaction.AccountFlagsTransactionData;
|
||||||
|
import org.qora.data.transaction.TransactionData;
|
||||||
|
import org.qora.repository.DataException;
|
||||||
|
import org.qora.repository.hsqldb.HSQLDBRepository;
|
||||||
|
import org.qora.repository.hsqldb.HSQLDBSaver;
|
||||||
|
|
||||||
|
public class HSQLDBAccountFlagsTransactionRepository extends HSQLDBTransactionRepository {
|
||||||
|
|
||||||
|
public HSQLDBAccountFlagsTransactionRepository(HSQLDBRepository repository) {
|
||||||
|
this.repository = repository;
|
||||||
|
}
|
||||||
|
|
||||||
|
TransactionData fromBase(long timestamp, int txGroupId, byte[] reference, byte[] creatorPublicKey, BigDecimal fee, byte[] signature) throws DataException {
|
||||||
|
try (ResultSet resultSet = this.repository
|
||||||
|
.checkedExecute("SELECT target, and_mask, or_mask, xor_mask, previous_flags FROM AccountFlagsTransactions WHERE signature = ?", signature)) {
|
||||||
|
if (resultSet == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
String target = resultSet.getString(1);
|
||||||
|
int andMask = resultSet.getInt(2);
|
||||||
|
int orMask = resultSet.getInt(3);
|
||||||
|
int xorMask = resultSet.getInt(4);
|
||||||
|
|
||||||
|
Integer previousFlags = resultSet.getInt(5);
|
||||||
|
if (resultSet.wasNull())
|
||||||
|
previousFlags = null;
|
||||||
|
|
||||||
|
return new AccountFlagsTransactionData(timestamp, txGroupId, reference, creatorPublicKey, target, andMask, orMask, xorMask, previousFlags, fee,
|
||||||
|
signature);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to fetch account flags transaction from repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(TransactionData transactionData) throws DataException {
|
||||||
|
AccountFlagsTransactionData accountFlagsTransactionData = (AccountFlagsTransactionData) transactionData;
|
||||||
|
|
||||||
|
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountFlagsTransactions");
|
||||||
|
|
||||||
|
saveHelper.bind("signature", accountFlagsTransactionData.getSignature()).bind("creator", accountFlagsTransactionData.getCreatorPublicKey())
|
||||||
|
.bind("target", accountFlagsTransactionData.getTarget()).bind("and_mask", accountFlagsTransactionData.getAndMask())
|
||||||
|
.bind("or_mask", accountFlagsTransactionData.getOrMask()).bind("xor_mask", accountFlagsTransactionData.getXorMask())
|
||||||
|
.bind("previous_flags", accountFlagsTransactionData.getPreviousFlags());
|
||||||
|
|
||||||
|
try {
|
||||||
|
saveHelper.execute(this.repository);
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DataException("Unable to save account flags transaction into repository", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
134
src/main/java/org/qora/transaction/AccountFlagsTransaction.java
Normal file
134
src/main/java/org/qora/transaction/AccountFlagsTransaction.java
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
package org.qora.transaction;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.qora.account.Account;
|
||||||
|
import org.qora.account.GenesisAccount;
|
||||||
|
import org.qora.asset.Asset;
|
||||||
|
import org.qora.data.transaction.AccountFlagsTransactionData;
|
||||||
|
import org.qora.data.transaction.TransactionData;
|
||||||
|
import org.qora.repository.DataException;
|
||||||
|
import org.qora.repository.Repository;
|
||||||
|
|
||||||
|
public class AccountFlagsTransaction extends Transaction {
|
||||||
|
|
||||||
|
// Properties
|
||||||
|
private AccountFlagsTransactionData accountFlagsTransactionData;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
public AccountFlagsTransaction(Repository repository, TransactionData transactionData) {
|
||||||
|
super(repository, transactionData);
|
||||||
|
|
||||||
|
this.accountFlagsTransactionData = (AccountFlagsTransactionData) this.transactionData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// More information
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Account> getRecipientAccounts() throws DataException {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInvolved(Account account) throws DataException {
|
||||||
|
String address = account.getAddress();
|
||||||
|
|
||||||
|
if (address.equals(this.getCreator().getAddress()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (address.equals(this.getTarget().getAddress()))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BigDecimal getAmount(Account account) throws DataException {
|
||||||
|
String address = account.getAddress();
|
||||||
|
BigDecimal amount = BigDecimal.ZERO.setScale(8);
|
||||||
|
|
||||||
|
if (address.equals(this.getCreator().getAddress()))
|
||||||
|
amount = amount.subtract(this.transactionData.getFee());
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
|
||||||
|
public Account getTarget() {
|
||||||
|
return new Account(this.repository, this.accountFlagsTransactionData.getTarget());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ValidationResult isValid() throws DataException {
|
||||||
|
Account creator = getCreator();
|
||||||
|
|
||||||
|
// Only genesis account can modify flags
|
||||||
|
if (!creator.getAddress().equals(new GenesisAccount(repository).getAddress()))
|
||||||
|
return ValidationResult.NO_FLAG_PERMISSION;
|
||||||
|
|
||||||
|
// Check fee is zero or positive
|
||||||
|
if (accountFlagsTransactionData.getFee().compareTo(BigDecimal.ZERO) < 0)
|
||||||
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
|
||||||
|
// Check reference
|
||||||
|
if (!Arrays.equals(creator.getLastReference(), accountFlagsTransactionData.getReference()))
|
||||||
|
return ValidationResult.INVALID_REFERENCE;
|
||||||
|
|
||||||
|
// Check creator has enough funds
|
||||||
|
if (creator.getConfirmedBalance(Asset.QORA).compareTo(accountFlagsTransactionData.getFee()) < 0)
|
||||||
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
|
return ValidationResult.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process() throws DataException {
|
||||||
|
Account target = getTarget();
|
||||||
|
int previousFlags = target.getFlags();
|
||||||
|
|
||||||
|
accountFlagsTransactionData.setPreviousFlags(previousFlags);
|
||||||
|
|
||||||
|
// Save this transaction with target account's previous flags value
|
||||||
|
this.repository.getTransactionRepository().save(accountFlagsTransactionData);
|
||||||
|
|
||||||
|
// Set account's new flags
|
||||||
|
int newFlags = previousFlags & accountFlagsTransactionData.getAndMask()
|
||||||
|
| accountFlagsTransactionData.getOrMask() ^ accountFlagsTransactionData.getXorMask();
|
||||||
|
|
||||||
|
target.setFlags(newFlags);
|
||||||
|
|
||||||
|
// Update creator's balance
|
||||||
|
Account creator = getCreator();
|
||||||
|
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).subtract(accountFlagsTransactionData.getFee()));
|
||||||
|
|
||||||
|
// Update creator's reference
|
||||||
|
creator.setLastReference(accountFlagsTransactionData.getSignature());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void orphan() throws DataException {
|
||||||
|
// Revert
|
||||||
|
Account target = getTarget();
|
||||||
|
|
||||||
|
target.setFlags(accountFlagsTransactionData.getPreviousFlags());
|
||||||
|
|
||||||
|
// Delete this transaction itself
|
||||||
|
this.repository.getTransactionRepository().delete(accountFlagsTransactionData);
|
||||||
|
|
||||||
|
Account creator = getCreator();
|
||||||
|
|
||||||
|
// Update creator's balance
|
||||||
|
creator.setConfirmedBalance(Asset.QORA, creator.getConfirmedBalance(Asset.QORA).add(accountFlagsTransactionData.getFee()));
|
||||||
|
|
||||||
|
// Update creator's reference
|
||||||
|
creator.setLastReference(accountFlagsTransactionData.getReference());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -72,7 +72,8 @@ public abstract class Transaction {
|
|||||||
LEAVE_GROUP(32, false),
|
LEAVE_GROUP(32, false),
|
||||||
GROUP_APPROVAL(33, false),
|
GROUP_APPROVAL(33, false),
|
||||||
SET_GROUP(34, false),
|
SET_GROUP(34, false),
|
||||||
UPDATE_ASSET(35, true);
|
UPDATE_ASSET(35, true),
|
||||||
|
ACCOUNT_FLAGS(36, false);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
public final boolean needsApproval;
|
public final boolean needsApproval;
|
||||||
@ -190,6 +191,7 @@ public abstract class Transaction {
|
|||||||
MULTIPLE_NAMES_FORBIDDEN(69),
|
MULTIPLE_NAMES_FORBIDDEN(69),
|
||||||
INVALID_ASSET_OWNER(70),
|
INVALID_ASSET_OWNER(70),
|
||||||
AT_IS_FINISHED(71),
|
AT_IS_FINISHED(71),
|
||||||
|
NO_FLAG_PERMISSION(72),
|
||||||
NOT_YET_RELEASED(1000);
|
NOT_YET_RELEASED(1000);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
package org.qora.transform.transaction;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import org.qora.block.BlockChain;
|
||||||
|
import org.qora.data.transaction.AccountFlagsTransactionData;
|
||||||
|
import org.qora.data.transaction.TransactionData;
|
||||||
|
import org.qora.transaction.Transaction.TransactionType;
|
||||||
|
import org.qora.transform.TransformationException;
|
||||||
|
import org.qora.utils.Serialization;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
|
|
||||||
|
public class AccountFlagsTransactionTransformer extends TransactionTransformer {
|
||||||
|
|
||||||
|
// Property lengths
|
||||||
|
private static final int TARGET_LENGTH = ADDRESS_LENGTH;
|
||||||
|
private static final int AND_MASK_LENGTH = INT_LENGTH;
|
||||||
|
private static final int OR_MASK_LENGTH = INT_LENGTH;
|
||||||
|
private static final int XOR_MASK_LENGTH = INT_LENGTH;
|
||||||
|
|
||||||
|
private static final int EXTRAS_LENGTH = TARGET_LENGTH + AND_MASK_LENGTH + OR_MASK_LENGTH + XOR_MASK_LENGTH;
|
||||||
|
|
||||||
|
protected static final TransactionLayout layout;
|
||||||
|
|
||||||
|
static {
|
||||||
|
layout = new TransactionLayout();
|
||||||
|
layout.add("txType: " + TransactionType.GROUP_INVITE.valueString, TransformationType.INT);
|
||||||
|
layout.add("timestamp", TransformationType.TIMESTAMP);
|
||||||
|
layout.add("transaction's groupID", TransformationType.INT);
|
||||||
|
layout.add("reference", TransformationType.SIGNATURE);
|
||||||
|
layout.add("account's public key", TransformationType.PUBLIC_KEY);
|
||||||
|
layout.add("target account's address", TransformationType.ADDRESS);
|
||||||
|
layout.add("flags AND mask", TransformationType.INT);
|
||||||
|
layout.add("flags OR mask", TransformationType.INT);
|
||||||
|
layout.add("flags XOR mask", TransformationType.INT);
|
||||||
|
layout.add("fee", TransformationType.AMOUNT);
|
||||||
|
layout.add("signature", TransformationType.SIGNATURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||||
|
long timestamp = byteBuffer.getLong();
|
||||||
|
|
||||||
|
int txGroupId = 0;
|
||||||
|
if (timestamp >= BlockChain.getInstance().getQoraV2Timestamp())
|
||||||
|
txGroupId = byteBuffer.getInt();
|
||||||
|
|
||||||
|
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||||
|
byteBuffer.get(reference);
|
||||||
|
|
||||||
|
byte[] creatorPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||||
|
|
||||||
|
String target = Serialization.deserializeAddress(byteBuffer);
|
||||||
|
|
||||||
|
int andMask = byteBuffer.getInt();
|
||||||
|
int orMask = byteBuffer.getInt();
|
||||||
|
int xorMask = byteBuffer.getInt();
|
||||||
|
|
||||||
|
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||||
|
|
||||||
|
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||||
|
byteBuffer.get(signature);
|
||||||
|
|
||||||
|
return new AccountFlagsTransactionData(timestamp, txGroupId, reference, creatorPublicKey, target, andMask, orMask, xorMask, fee, signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||||
|
return getBaseLength(transactionData) + EXTRAS_LENGTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||||
|
try {
|
||||||
|
AccountFlagsTransactionData accountFlagsTransactionData = (AccountFlagsTransactionData) transactionData;
|
||||||
|
|
||||||
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
transformCommonBytes(transactionData, bytes);
|
||||||
|
|
||||||
|
Serialization.serializeAddress(bytes, accountFlagsTransactionData.getTarget());
|
||||||
|
|
||||||
|
bytes.write(Ints.toByteArray(accountFlagsTransactionData.getAndMask()));
|
||||||
|
bytes.write(Ints.toByteArray(accountFlagsTransactionData.getOrMask()));
|
||||||
|
bytes.write(Ints.toByteArray(accountFlagsTransactionData.getXorMask()));
|
||||||
|
|
||||||
|
Serialization.serializeBigDecimal(bytes, accountFlagsTransactionData.getFee());
|
||||||
|
|
||||||
|
if (accountFlagsTransactionData.getSignature() != null)
|
||||||
|
bytes.write(accountFlagsTransactionData.getSignature());
|
||||||
|
|
||||||
|
return bytes.toByteArray();
|
||||||
|
} catch (IOException | ClassCastException e) {
|
||||||
|
throw new TransformationException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -103,7 +103,7 @@ public class TransactionTests extends Common {
|
|||||||
|
|
||||||
// Create test generator account
|
// Create test generator account
|
||||||
generator = new PrivateKeyAccount(repository, generatorSeed);
|
generator = new PrivateKeyAccount(repository, generatorSeed);
|
||||||
accountRepository.setLastReference(new AccountData(generator.getAddress(), generatorSeed, generator.getPublicKey(), Group.NO_GROUP));
|
accountRepository.setLastReference(new AccountData(generator.getAddress(), generatorSeed, generator.getPublicKey(), Group.NO_GROUP, 0));
|
||||||
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, initialGeneratorBalance));
|
accountRepository.save(new AccountBalanceData(generator.getAddress(), Asset.QORA, initialGeneratorBalance));
|
||||||
|
|
||||||
// Create test sender account
|
// Create test sender account
|
||||||
@ -111,7 +111,7 @@ public class TransactionTests extends Common {
|
|||||||
|
|
||||||
// Mock account
|
// Mock account
|
||||||
reference = senderSeed;
|
reference = senderSeed;
|
||||||
accountRepository.setLastReference(new AccountData(sender.getAddress(), reference, sender.getPublicKey(), Group.NO_GROUP));
|
accountRepository.setLastReference(new AccountData(sender.getAddress(), reference, sender.getPublicKey(), Group.NO_GROUP, 0));
|
||||||
|
|
||||||
// Mock balance
|
// Mock balance
|
||||||
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, initialSenderBalance));
|
accountRepository.save(new AccountBalanceData(sender.getAddress(), Asset.QORA, initialSenderBalance));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user