Fix up some group-approval related issues

UPDATE_NAME requires txGroupId to match original txGroupId used in REGISTER_NAME
UPDATE_GROUP requires txGroupId to match original txGroupId used in CREATE_GROUP

Extraneous JAXB unmarshal code removed from various transaction constructors.
Incorrect, old, groupID vs txGroupId checks removed from various transactions.

Fix bug in UPDATE_GROUP fromBase() method using mismatched column indexes.

Fix bug in API's pending transaction fetch not checking admin is of the same
group as the txGroupId. (Fix actually in HSQLDBTransactionRepository).
Similar fix in GroupApprovalTransaction.
This commit is contained in:
catbref 2019-02-26 16:52:22 +00:00
parent c7123df79d
commit ec5eba9c60
29 changed files with 104 additions and 108 deletions

View File

@ -28,6 +28,12 @@ public class GroupData {
@XmlTransient @XmlTransient
@Schema(hidden = true) @Schema(hidden = true)
private byte[] reference; private byte[] reference;
// For internal use
@XmlTransient
@Schema(
hidden = true
)
private int creationGroupId;
// Constructors // Constructors
@ -36,7 +42,7 @@ public class GroupData {
} }
/** Constructs new GroupData with nullable groupId and nullable updated [timestamp] */ /** Constructs new GroupData with nullable groupId and nullable updated [timestamp] */
public GroupData(Integer groupId, String owner, String name, String description, long created, Long updated, boolean isOpen, ApprovalThreshold approvalThreshold, int minBlockDelay, int maxBlockDelay, byte[] reference) { public GroupData(Integer groupId, String owner, String name, String description, long created, Long updated, boolean isOpen, ApprovalThreshold approvalThreshold, int minBlockDelay, int maxBlockDelay, byte[] reference, int creationGroupId) {
this.groupId = groupId; this.groupId = groupId;
this.owner = owner; this.owner = owner;
this.groupName = name; this.groupName = name;
@ -48,11 +54,12 @@ public class GroupData {
this.reference = reference; this.reference = reference;
this.minimumBlockDelay = minBlockDelay; this.minimumBlockDelay = minBlockDelay;
this.maximumBlockDelay = maxBlockDelay; this.maximumBlockDelay = maxBlockDelay;
this.creationGroupId = creationGroupId;
} }
/** Constructs new GroupData with unassigned groupId */ /** Constructs new GroupData with unassigned groupId */
public GroupData(String owner, String name, String description, long created, boolean isOpen, ApprovalThreshold approvalThreshold, int minBlockDelay, int maxBlockDelay, byte[] reference) { public GroupData(String owner, String name, String description, long created, boolean isOpen, ApprovalThreshold approvalThreshold, int minBlockDelay, int maxBlockDelay, byte[] reference, int creationGroupId) {
this(null, owner, name, description, created, null, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference); this(null, owner, name, description, created, null, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference, creationGroupId);
} }
// Getters / setters // Getters / setters
@ -129,4 +136,8 @@ public class GroupData {
return this.maximumBlockDelay; return this.maximumBlockDelay;
} }
public int getCreationGroupId() {
return this.creationGroupId;
}
} }

View File

@ -20,10 +20,18 @@ public class NameData {
private Long updated; private Long updated;
// No need to expose this via API // No need to expose this via API
@XmlTransient @XmlTransient
@Schema(hidden = true) @Schema(
hidden = true
)
private byte[] reference; private byte[] reference;
private boolean isForSale; private boolean isForSale;
private BigDecimal salePrice; private BigDecimal salePrice;
// For internal use
@XmlTransient
@Schema(
hidden = true
)
private int creationGroupId;
// Constructors // Constructors
@ -31,8 +39,8 @@ public class NameData {
protected NameData() { protected NameData() {
} }
public NameData(String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale, public NameData(String owner, String name, String data, long registered, Long updated, byte[] reference, boolean isForSale, BigDecimal salePrice,
BigDecimal salePrice) { int creationGroupId) {
this.owner = owner; this.owner = owner;
this.name = name; this.name = name;
this.data = data; this.data = data;
@ -41,10 +49,11 @@ public class NameData {
this.reference = reference; this.reference = reference;
this.isForSale = isForSale; this.isForSale = isForSale;
this.salePrice = salePrice; this.salePrice = salePrice;
this.creationGroupId = creationGroupId;
} }
public NameData(String owner, String name, String data, long registered, byte[] reference) { public NameData(String owner, String name, String data, long registered, byte[] reference, int creationGroupId) {
this(owner, name, data, registered, null, reference, false, null); this(owner, name, data, registered, null, reference, false, null, creationGroupId);
} }
// Getters / setters // Getters / setters
@ -105,4 +114,8 @@ public class NameData {
this.salePrice = salePrice; this.salePrice = salePrice;
} }
public int getCreationGroupId() {
return this.creationGroupId;
}
} }

View File

@ -46,6 +46,11 @@ public class IssueAssetTransactionData extends TransactionData {
} }
public void afterUnmarshal(Unmarshaller u, Object parent) { 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 issuer's public key is supplied
* then use genesis account's public key.
*/
if (parent instanceof GenesisBlock.GenesisInfo && this.issuerPublicKey == null) if (parent instanceof GenesisBlock.GenesisInfo && this.issuerPublicKey == null)
this.issuerPublicKey = GenesisAccount.PUBLIC_KEY; this.issuerPublicKey = GenesisAccount.PUBLIC_KEY;

View File

@ -116,7 +116,7 @@ public class Group {
this.groupData = new GroupData(createGroupTransactionData.getOwner(), createGroupTransactionData.getGroupName(), this.groupData = new GroupData(createGroupTransactionData.getOwner(), createGroupTransactionData.getGroupName(),
createGroupTransactionData.getDescription(), createGroupTransactionData.getTimestamp(), createGroupTransactionData.getIsOpen(), createGroupTransactionData.getDescription(), createGroupTransactionData.getTimestamp(), createGroupTransactionData.getIsOpen(),
createGroupTransactionData.getApprovalThreshold(), createGroupTransactionData.getMinimumBlockDelay(), createGroupTransactionData.getApprovalThreshold(), createGroupTransactionData.getMinimumBlockDelay(),
createGroupTransactionData.getMaximumBlockDelay(), createGroupTransactionData.getSignature()); createGroupTransactionData.getMaximumBlockDelay(), createGroupTransactionData.getSignature(), createGroupTransactionData.getTxGroupId());
} }
/** /**

View File

@ -35,7 +35,7 @@ public class Name {
this.repository = repository; this.repository = repository;
this.nameData = new NameData(registerNameTransactionData.getOwner(), this.nameData = new NameData(registerNameTransactionData.getOwner(),
registerNameTransactionData.getName(), registerNameTransactionData.getData(), registerNameTransactionData.getTimestamp(), registerNameTransactionData.getName(), registerNameTransactionData.getData(), registerNameTransactionData.getTimestamp(),
registerNameTransactionData.getSignature()); registerNameTransactionData.getSignature(), registerNameTransactionData.getTxGroupId());
} }
/** /**

View File

@ -568,6 +568,21 @@ public class HSQLDBDatabaseUpdates {
stmt.execute("ALTER TABLE UpdateGroupTransactions ADD COLUMN new_max_block_delay INT NOT NULL DEFAULT 1440 BEFORE group_reference"); stmt.execute("ALTER TABLE UpdateGroupTransactions ADD COLUMN new_max_block_delay INT NOT NULL DEFAULT 1440 BEFORE group_reference");
break; break;
case 36:
// Adding group-ness to record types that could require approval for their related transactions
// e.g. REGISTER_NAME might require approval and so Names table requires groupID
// Registered Names
stmt.execute("ALTER TABLE Names ADD COLUMN creation_group_id GroupID NOT NULL DEFAULT 0");
// Assets aren't ever updated so don't need group-ness
// for future use: stmt.execute("ALTER TABLE Assets ADD COLUMN creation_group_id GroupID NOT NULL DEFAULT 0");
// Polls aren't ever updated, only voted upon using option index so don't need group-ness
// for future use: stmt.execute("ALTER TABLE Polls ADD COLUMN creation_group_id GroupID NOT NULL DEFAULT 0");
// CIYAM ATs
stmt.execute("ALTER TABLE ATs ADD COLUMN creation_group_id GroupID NOT NULL DEFAULT 0");
// Groups can be updated but updates require approval from original groupID
stmt.execute("ALTER TABLE Groups ADD COLUMN creation_group_id GroupID NOT NULL DEFAULT 0");
break;
default: default:
// nothing to do // nothing to do
return false; return false;

View File

@ -30,7 +30,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override @Override
public GroupData fromGroupId(int groupId) throws DataException { public GroupData fromGroupId(int groupId) throws DataException {
try (ResultSet resultSet = this.repository try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_name, owner, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay FROM Groups WHERE group_id = ?", groupId)) { .checkedExecute("SELECT group_name, owner, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay, creation_group_id FROM Groups WHERE group_id = ?", groupId)) {
if (resultSet == null) if (resultSet == null)
return null; return null;
@ -51,7 +51,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
int minBlockDelay = resultSet.getInt(9); int minBlockDelay = resultSet.getInt(9);
int maxBlockDelay = resultSet.getInt(10); int maxBlockDelay = resultSet.getInt(10);
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference); int creationGroupId = resultSet.getInt(11);
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference, creationGroupId);
} catch (SQLException e) { } catch (SQLException e) {
throw new DataException("Unable to fetch group info from repository", e); throw new DataException("Unable to fetch group info from repository", e);
} }
@ -60,7 +62,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override @Override
public GroupData fromGroupName(String groupName) throws DataException { public GroupData fromGroupName(String groupName) throws DataException {
try (ResultSet resultSet = this.repository try (ResultSet resultSet = this.repository
.checkedExecute("SELECT group_id, owner, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay FROM Groups WHERE group_name = ?", groupName)) { .checkedExecute("SELECT group_id, owner, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay, creation_group_id FROM Groups WHERE group_name = ?", groupName)) {
if (resultSet == null) if (resultSet == null)
return null; return null;
@ -81,7 +83,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
int minBlockDelay = resultSet.getInt(9); int minBlockDelay = resultSet.getInt(9);
int maxBlockDelay = resultSet.getInt(10); int maxBlockDelay = resultSet.getInt(10);
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference); int creationGroupId = resultSet.getInt(11);
return new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference, creationGroupId);
} catch (SQLException e) { } catch (SQLException e) {
throw new DataException("Unable to fetch group info from repository", e); throw new DataException("Unable to fetch group info from repository", e);
} }
@ -107,7 +111,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override @Override
public List<GroupData> getAllGroups(Integer limit, Integer offset, Boolean reverse) throws DataException { public List<GroupData> getAllGroups(Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay FROM Groups ORDER BY group_name"; String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay, creation_group_id FROM Groups ORDER BY group_name";
if (reverse != null && reverse) if (reverse != null && reverse)
sql += " DESC"; sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset); sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -137,7 +141,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
int minBlockDelay = resultSet.getInt(10); int minBlockDelay = resultSet.getInt(10);
int maxBlockDelay = resultSet.getInt(11); int maxBlockDelay = resultSet.getInt(11);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference)); int creationGroupId = resultSet.getInt(12);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference, creationGroupId));
} while (resultSet.next()); } while (resultSet.next());
return groups; return groups;
@ -148,7 +154,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override @Override
public List<GroupData> getGroupsByOwner(String owner, Integer limit, Integer offset, Boolean reverse) throws DataException { public List<GroupData> getGroupsByOwner(String owner, Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT group_id, group_name, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay FROM Groups WHERE owner = ? ORDER BY group_name"; String sql = "SELECT group_id, group_name, description, created, updated, reference, is_open, approval_threshold, min_block_delay, max_block_delay, creation_group_id FROM Groups WHERE owner = ? ORDER BY group_name";
if (reverse != null && reverse) if (reverse != null && reverse)
sql += " DESC"; sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset); sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -177,7 +183,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
int minBlockDelay = resultSet.getInt(9); int minBlockDelay = resultSet.getInt(9);
int maxBlockDelay = resultSet.getInt(10); int maxBlockDelay = resultSet.getInt(10);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference)); int creationGroupId = resultSet.getInt(11);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference, creationGroupId));
} while (resultSet.next()); } while (resultSet.next());
return groups; return groups;
@ -188,7 +196,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
@Override @Override
public List<GroupData> getGroupsWithMember(String member, Integer limit, Integer offset, Boolean reverse) throws DataException { public List<GroupData> getGroupsWithMember(String member, Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open, approval_threshold min_block_delay, max_block_delay FROM Groups " String sql = "SELECT group_id, owner, group_name, description, created, updated, reference, is_open, approval_threshold min_block_delay, max_block_delay, creation_group_id FROM Groups "
+ "JOIN GroupMembers USING (group_id) WHERE address = ? ORDER BY group_name"; + "JOIN GroupMembers USING (group_id) WHERE address = ? ORDER BY group_name";
if (reverse != null && reverse) if (reverse != null && reverse)
sql += " DESC"; sql += " DESC";
@ -219,7 +227,9 @@ public class HSQLDBGroupRepository implements GroupRepository {
int minBlockDelay = resultSet.getInt(10); int minBlockDelay = resultSet.getInt(10);
int maxBlockDelay = resultSet.getInt(11); int maxBlockDelay = resultSet.getInt(11);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference)); int creationGroupId = resultSet.getInt(12);
groups.add(new GroupData(groupId, owner, groupName, description, created, updated, isOpen, approvalThreshold, minBlockDelay, maxBlockDelay, reference, creationGroupId));
} while (resultSet.next()); } while (resultSet.next());
return groups; return groups;
@ -239,7 +249,8 @@ public class HSQLDBGroupRepository implements GroupRepository {
saveHelper.bind("group_id", groupData.getGroupId()).bind("owner", groupData.getOwner()).bind("group_name", groupData.getGroupName()) saveHelper.bind("group_id", groupData.getGroupId()).bind("owner", groupData.getOwner()).bind("group_name", groupData.getGroupName())
.bind("description", groupData.getDescription()).bind("created", new Timestamp(groupData.getCreated())).bind("updated", updatedTimestamp) .bind("description", groupData.getDescription()).bind("created", new Timestamp(groupData.getCreated())).bind("updated", updatedTimestamp)
.bind("reference", groupData.getReference()).bind("is_open", groupData.getIsOpen()).bind("approval_threshold", groupData.getApprovalThreshold().value) .bind("reference", groupData.getReference()).bind("is_open", groupData.getIsOpen()).bind("approval_threshold", groupData.getApprovalThreshold().value)
.bind("min_block_delay", groupData.getMinimumBlockDelay()).bind("max_block_delay", groupData.getMaximumBlockDelay()); .bind("min_block_delay", groupData.getMinimumBlockDelay()).bind("max_block_delay", groupData.getMaximumBlockDelay())
.bind("creation_group_id", groupData.getCreationGroupId());
try { try {
saveHelper.execute(this.repository); saveHelper.execute(this.repository);

View File

@ -23,7 +23,7 @@ public class HSQLDBNameRepository implements NameRepository {
@Override @Override
public NameData fromName(String name) throws DataException { public NameData fromName(String name) throws DataException {
try (ResultSet resultSet = this.repository try (ResultSet resultSet = this.repository
.checkedExecute("SELECT owner, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE name = ?", name)) { .checkedExecute("SELECT owner, data, registered, updated, reference, is_for_sale, sale_price, creation_group_id FROM Names WHERE name = ?", name)) {
if (resultSet == null) if (resultSet == null)
return null; return null;
@ -38,8 +38,9 @@ public class HSQLDBNameRepository implements NameRepository {
byte[] reference = resultSet.getBytes(5); byte[] reference = resultSet.getBytes(5);
boolean isForSale = resultSet.getBoolean(6); boolean isForSale = resultSet.getBoolean(6);
BigDecimal salePrice = resultSet.getBigDecimal(7); BigDecimal salePrice = resultSet.getBigDecimal(7);
int creationGroupId = resultSet.getInt(8);
return new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice); return new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId);
} catch (SQLException e) { } catch (SQLException e) {
throw new DataException("Unable to fetch name info from repository", e); throw new DataException("Unable to fetch name info from repository", e);
} }
@ -56,7 +57,7 @@ public class HSQLDBNameRepository implements NameRepository {
@Override @Override
public List<NameData> getAllNames(Integer limit, Integer offset, Boolean reverse) throws DataException { public List<NameData> getAllNames(Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT name, data, owner, registered, updated, reference, is_for_sale, sale_price FROM Names ORDER BY name"; String sql = "SELECT name, data, owner, registered, updated, reference, is_for_sale, sale_price, creation_group_id FROM Names ORDER BY name";
if (reverse != null && reverse) if (reverse != null && reverse)
sql += " DESC"; sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset); sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -80,8 +81,9 @@ public class HSQLDBNameRepository implements NameRepository {
byte[] reference = resultSet.getBytes(6); byte[] reference = resultSet.getBytes(6);
boolean isForSale = resultSet.getBoolean(7); boolean isForSale = resultSet.getBoolean(7);
BigDecimal salePrice = resultSet.getBigDecimal(8); BigDecimal salePrice = resultSet.getBigDecimal(8);
int creationGroupId = resultSet.getInt(9);
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice)); names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId));
} while (resultSet.next()); } while (resultSet.next());
return names; return names;
@ -92,7 +94,7 @@ public class HSQLDBNameRepository implements NameRepository {
@Override @Override
public List<NameData> getNamesForSale(Integer limit, Integer offset, Boolean reverse) throws DataException { public List<NameData> getNamesForSale(Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT name, data, owner, registered, updated, reference, sale_price FROM Names WHERE is_for_sale = TRUE ORDER BY name"; String sql = "SELECT name, data, owner, registered, updated, reference, sale_price, creation_group_id FROM Names WHERE is_for_sale = TRUE ORDER BY name";
if (reverse != null && reverse) if (reverse != null && reverse)
sql += " DESC"; sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset); sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -116,8 +118,9 @@ public class HSQLDBNameRepository implements NameRepository {
byte[] reference = resultSet.getBytes(6); byte[] reference = resultSet.getBytes(6);
boolean isForSale = true; boolean isForSale = true;
BigDecimal salePrice = resultSet.getBigDecimal(7); BigDecimal salePrice = resultSet.getBigDecimal(7);
int creationGroupId = resultSet.getInt(8);
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice)); names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId));
} while (resultSet.next()); } while (resultSet.next());
return names; return names;
@ -128,7 +131,7 @@ public class HSQLDBNameRepository implements NameRepository {
@Override @Override
public List<NameData> getNamesByOwner(String owner, Integer limit, Integer offset, Boolean reverse) throws DataException { public List<NameData> getNamesByOwner(String owner, Integer limit, Integer offset, Boolean reverse) throws DataException {
String sql = "SELECT name, data, registered, updated, reference, is_for_sale, sale_price FROM Names WHERE owner = ? ORDER BY name"; String sql = "SELECT name, data, registered, updated, reference, is_for_sale, sale_price, creation_group_id FROM Names WHERE owner = ? ORDER BY name";
if (reverse != null && reverse) if (reverse != null && reverse)
sql += " DESC"; sql += " DESC";
sql += HSQLDBRepository.limitOffsetSql(limit, offset); sql += HSQLDBRepository.limitOffsetSql(limit, offset);
@ -151,8 +154,9 @@ public class HSQLDBNameRepository implements NameRepository {
byte[] reference = resultSet.getBytes(5); byte[] reference = resultSet.getBytes(5);
boolean isForSale = resultSet.getBoolean(6); boolean isForSale = resultSet.getBoolean(6);
BigDecimal salePrice = resultSet.getBigDecimal(7); BigDecimal salePrice = resultSet.getBigDecimal(7);
int creationGroupId = resultSet.getInt(8);
names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice)); names.add(new NameData(owner, name, data, registered, updated, reference, isForSale, salePrice, creationGroupId));
} while (resultSet.next()); } while (resultSet.next());
return names; return names;
@ -171,7 +175,8 @@ public class HSQLDBNameRepository implements NameRepository {
saveHelper.bind("owner", nameData.getOwner()).bind("name", nameData.getName()).bind("data", nameData.getData()) saveHelper.bind("owner", nameData.getOwner()).bind("name", nameData.getName()).bind("data", nameData.getData())
.bind("registered", new Timestamp(nameData.getRegistered())).bind("updated", updatedTimestamp).bind("reference", nameData.getReference()) .bind("registered", new Timestamp(nameData.getRegistered())).bind("updated", updatedTimestamp).bind("reference", nameData.getReference())
.bind("is_for_sale", nameData.getIsForSale()).bind("sale_price", nameData.getSalePrice()); .bind("is_for_sale", nameData.getIsForSale()).bind("sale_price", nameData.getSalePrice())
.bind("creation_group_id", nameData.getCreationGroupId());
try { try {
saveHelper.execute(this.repository); saveHelper.execute(this.repository);

View File

@ -508,7 +508,7 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
String sql = "SELECT signature FROM UnconfirmedTransactions " String sql = "SELECT signature FROM UnconfirmedTransactions "
+ "NATURAL JOIN Transactions " + "NATURAL JOIN Transactions "
+ "LEFT OUTER JOIN Accounts ON Accounts.public_key = Transactions.creator " + "LEFT OUTER JOIN Accounts ON Accounts.public_key = Transactions.creator "
+ "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account " + "LEFT OUTER JOIN GroupAdmins ON GroupAdmins.admin = Accounts.account AND GroupAdmins.group_id = Transactions.tx_group_id "
+ "WHERE Transactions.tx_group_id != ? AND GroupAdmins.admin IS NULL " + "WHERE Transactions.tx_group_id != ? AND GroupAdmins.admin IS NULL "
+ "AND Transactions.type IN (" + txTypes + ") " + "AND Transactions.type IN (" + txTypes + ") "
+ "ORDER BY creation"; + "ORDER BY creation";

View File

@ -29,9 +29,9 @@ public class HSQLDBUpdateGroupTransactionRepository extends HSQLDBTransactionRep
String newDescription = resultSet.getString(3); String newDescription = resultSet.getString(3);
boolean newIsOpen = resultSet.getBoolean(4); boolean newIsOpen = resultSet.getBoolean(4);
ApprovalThreshold newApprovalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(5)); ApprovalThreshold newApprovalThreshold = ApprovalThreshold.valueOf(resultSet.getInt(5));
byte[] groupReference = resultSet.getBytes(6); int newMinBlockDelay = resultSet.getInt(6);
int newMinBlockDelay = resultSet.getInt(7); int newMaxBlockDelay = resultSet.getInt(7);
int newMaxBlockDelay = resultSet.getInt(8); byte[] groupReference = resultSet.getBytes(8);
return new UpdateGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, newOwner, newDescription, newIsOpen, return new UpdateGroupTransactionData(timestamp, txGroupId, reference, creatorPublicKey, groupId, newOwner, newDescription, newIsOpen,
newApprovalThreshold, newMinBlockDelay, newMaxBlockDelay, groupReference, fee, signature); newApprovalThreshold, newMinBlockDelay, newMaxBlockDelay, groupReference, fee, signature);

View File

@ -84,10 +84,6 @@ public class AddGroupAdminTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != addGroupAdminTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account owner = getOwner(); Account owner = getOwner();
// Check transaction's public key matches group's current owner // Check transaction's public key matches group's current owner

View File

@ -28,10 +28,6 @@ public class BuyNameTransaction extends Transaction {
super(repository, transactionData); super(repository, transactionData);
this.buyNameTransactionData = (BuyNameTransactionData) this.transactionData; this.buyNameTransactionData = (BuyNameTransactionData) this.transactionData;
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
if (this.transactionData.getCreatorPublicKey() == null)
this.transactionData.setCreatorPublicKey(this.buyNameTransactionData.getBuyerPublicKey());
} }
// More information // More information

View File

@ -84,10 +84,6 @@ public class CancelGroupBanTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != groupUnbanTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin(); Account admin = getAdmin();
// Can't unban if not an admin // Can't unban if not an admin

View File

@ -84,10 +84,6 @@ public class CancelGroupInviteTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != cancelGroupInviteTransactionData.getGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin(); Account admin = getAdmin();
// Check admin is actually an admin // Check admin is actually an admin

View File

@ -28,10 +28,6 @@ public class CancelSellNameTransaction extends Transaction {
super(repository, transactionData); super(repository, transactionData);
this.cancelSellNameTransactionData = (CancelSellNameTransactionData) this.transactionData; this.cancelSellNameTransactionData = (CancelSellNameTransactionData) this.transactionData;
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
if (this.transactionData.getCreatorPublicKey() == null)
this.transactionData.setCreatorPublicKey(this.cancelSellNameTransactionData.getOwnerPublicKey());
} }
// More information // More information

View File

@ -69,10 +69,6 @@ public class GroupApprovalTransaction extends Transaction {
if (pendingTransactionData == null) if (pendingTransactionData == null)
return ValidationResult.TRANSACTION_UNKNOWN; return ValidationResult.TRANSACTION_UNKNOWN;
// Check pending transaction's groupID matches our transaction's groupID
if (groupApprovalTransactionData.getTxGroupId() != pendingTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
// Check pending transaction is not already in a block // Check pending transaction is not already in a block
if (this.repository.getTransactionRepository().getHeightFromSignature(groupApprovalTransactionData.getPendingSignature()) != 0) if (this.repository.getTransactionRepository().getHeightFromSignature(groupApprovalTransactionData.getPendingSignature()) != 0)
return ValidationResult.TRANSACTION_ALREADY_CONFIRMED; return ValidationResult.TRANSACTION_ALREADY_CONFIRMED;
@ -80,7 +76,7 @@ public class GroupApprovalTransaction extends Transaction {
Account admin = getAdmin(); Account admin = getAdmin();
// Can't cast approval decision if not an admin // Can't cast approval decision if not an admin
if (!this.repository.getGroupRepository().adminExists(groupApprovalTransactionData.getTxGroupId(), admin.getAddress())) if (!this.repository.getGroupRepository().adminExists(pendingTransactionData.getTxGroupId(), admin.getAddress()))
return ValidationResult.NOT_GROUP_ADMIN; return ValidationResult.NOT_GROUP_ADMIN;
// Check fee is positive // Check fee is positive

View File

@ -84,10 +84,6 @@ public class GroupBanTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != groupBanTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin(); Account admin = getAdmin();
// Can't ban if not an admin // Can't ban if not an admin

View File

@ -75,10 +75,6 @@ public class GroupInviteTransaction extends Transaction {
public ValidationResult isValid() throws DataException { public ValidationResult isValid() throws DataException {
int groupId = groupInviteTransactionData.getGroupId(); int groupId = groupInviteTransactionData.getGroupId();
// Check transaction's groupID matches group's ID
if (groupInviteTransactionData.getTxGroupId() != groupId)
return ValidationResult.GROUP_ID_MISMATCH;
// Check time to live zero (infinite) or positive // Check time to live zero (infinite) or positive
if (groupInviteTransactionData.getTimeToLive() < 0) if (groupInviteTransactionData.getTimeToLive() < 0)
return ValidationResult.INVALID_LIFETIME; return ValidationResult.INVALID_LIFETIME;

View File

@ -87,10 +87,6 @@ public class GroupKickTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != groupKickTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account admin = getAdmin(); Account admin = getAdmin();
// Can't kick if not an admin // Can't kick if not an admin

View File

@ -32,10 +32,6 @@ public class IssueAssetTransaction extends Transaction {
super(repository, transactionData); super(repository, transactionData);
this.issueAssetTransactionData = (IssueAssetTransactionData) this.transactionData; this.issueAssetTransactionData = (IssueAssetTransactionData) this.transactionData;
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
if (this.transactionData.getCreatorPublicKey() == null)
this.transactionData.setCreatorPublicKey(this.issueAssetTransactionData.getIssuerPublicKey());
} }
// More information // More information

View File

@ -67,11 +67,6 @@ public class JoinGroupTransaction extends Transaction {
public ValidationResult isValid() throws DataException { public ValidationResult isValid() throws DataException {
int groupId = joinGroupTransactionData.getGroupId(); int groupId = joinGroupTransactionData.getGroupId();
// Check txGroupId
int txGroupId = joinGroupTransactionData.getTxGroupId();
if (txGroupId != Group.NO_GROUP && txGroupId != groupId)
return ValidationResult.GROUP_ID_MISMATCH;
// Check group exists // Check group exists
if (!this.repository.getGroupRepository().groupExists(groupId)) if (!this.repository.getGroupRepository().groupExists(groupId))
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;

View File

@ -72,10 +72,6 @@ public class LeaveGroupTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != leaveGroupTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account leaver = getLeaver(); Account leaver = getLeaver();
// Can't leave if group owner // Can't leave if group owner

View File

@ -26,10 +26,6 @@ public class PaymentTransaction extends Transaction {
super(repository, transactionData); super(repository, transactionData);
this.paymentTransactionData = (PaymentTransactionData) this.transactionData; this.paymentTransactionData = (PaymentTransactionData) this.transactionData;
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
if (this.transactionData.getCreatorPublicKey() == null)
this.transactionData.setCreatorPublicKey(this.paymentTransactionData.getSenderPublicKey());
} }
// More information // More information

View File

@ -28,10 +28,6 @@ public class RegisterNameTransaction extends Transaction {
super(repository, transactionData); super(repository, transactionData);
this.registerNameTransactionData = (RegisterNameTransactionData) this.transactionData; this.registerNameTransactionData = (RegisterNameTransactionData) this.transactionData;
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
if (this.transactionData.getCreatorPublicKey() == null)
this.transactionData.setCreatorPublicKey(this.registerNameTransactionData.getRegistrantPublicKey());
} }
// More information // More information

View File

@ -84,10 +84,6 @@ public class RemoveGroupAdminTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID
if (groupData.getGroupId() != removeGroupAdminTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH;
Account owner = getOwner(); Account owner = getOwner();
// Check transaction's public key matches group's current owner // Check transaction's public key matches group's current owner

View File

@ -29,10 +29,6 @@ public class SellNameTransaction extends Transaction {
super(repository, transactionData); super(repository, transactionData);
this.sellNameTransactionData = (SellNameTransactionData) this.transactionData; this.sellNameTransactionData = (SellNameTransactionData) this.transactionData;
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
if (this.transactionData.getCreatorPublicKey() == null)
this.transactionData.setCreatorPublicKey(this.sellNameTransactionData.getOwnerPublicKey());
} }
// More information // More information

View File

@ -185,6 +185,7 @@ public abstract class Transaction {
TRANSACTION_UNKNOWN(65), TRANSACTION_UNKNOWN(65),
TRANSACTION_ALREADY_CONFIRMED(66), TRANSACTION_ALREADY_CONFIRMED(66),
INVALID_TX_GROUP_ID(67), INVALID_TX_GROUP_ID(67),
TX_GROUP_ID_MISMATCH(68),
NOT_YET_RELEASED(1000); NOT_YET_RELEASED(1000);
public final int value; public final int value;
@ -665,7 +666,7 @@ public abstract class Transaction {
// Group no longer exists? Possibly due to blockchain orphaning undoing group creation? // Group no longer exists? Possibly due to blockchain orphaning undoing group creation?
return true; // stops tx being included in block but it will eventually expire return true; // stops tx being included in block but it will eventually expire
// If transaction's creator is group admin then auto-approve // If transaction's creator is group admin (of group with ID txGroupId) then auto-approve
PublicKeyAccount creator = this.getCreator(); PublicKeyAccount creator = this.getCreator();
if (groupRepository.adminExists(txGroupId, creator.getAddress())) if (groupRepository.adminExists(txGroupId, creator.getAddress()))
return false; return false;

View File

@ -95,9 +95,9 @@ public class UpdateGroupTransaction extends Transaction {
if (groupData == null) if (groupData == null)
return ValidationResult.GROUP_DOES_NOT_EXIST; return ValidationResult.GROUP_DOES_NOT_EXIST;
// Check transaction's groupID matches group's ID // As this transaction type could require approval, check txGroupId matches groupID at creation
if (groupData.getGroupId() != updateGroupTransactionData.getTxGroupId()) if (groupData.getCreationGroupId() != updateGroupTransactionData.getTxGroupId())
return ValidationResult.GROUP_ID_MISMATCH; return ValidationResult.TX_GROUP_ID_MISMATCH;
Account owner = getOwner(); Account owner = getOwner();

View File

@ -29,10 +29,6 @@ public class UpdateNameTransaction extends Transaction {
super(repository, transactionData); super(repository, transactionData);
this.updateNameTransactionData = (UpdateNameTransactionData) this.transactionData; this.updateNameTransactionData = (UpdateNameTransactionData) this.transactionData;
// XXX This is horrible - thanks to JAXB unmarshalling not calling constructor
if (this.transactionData.getCreatorPublicKey() == null)
this.transactionData.setCreatorPublicKey(this.updateNameTransactionData.getOwnerPublicKey());
} }
// More information // More information
@ -104,6 +100,10 @@ public class UpdateNameTransaction extends Transaction {
if (nameData == null) if (nameData == null)
return ValidationResult.NAME_DOES_NOT_EXIST; return ValidationResult.NAME_DOES_NOT_EXIST;
// As this transaction type could require approval, check txGroupId matches groupID at creation
if (nameData.getCreationGroupId() != updateNameTransactionData.getTxGroupId())
return ValidationResult.TX_GROUP_ID_MISMATCH;
// Check name isn't currently for sale // Check name isn't currently for sale
if (nameData.getIsForSale()) if (nameData.getIsForSale())
return ValidationResult.NAME_ALREADY_FOR_SALE; return ValidationResult.NAME_ALREADY_FOR_SALE;