forked from Qortal/qortal
Account group invite/cancel-invite and kick
Account group join for a group that is closed/invite-only and has no corresponding invite is now turned into a "join request". This can be accepted by an admin sending a corresponding invite tx.
This commit is contained in:
parent
df730d9fb9
commit
90f1676c7c
@ -29,9 +29,14 @@ import org.qora.api.model.GroupWithMemberInfo;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.group.GroupAdminData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.group.GroupInviteData;
|
||||
import org.qora.data.group.GroupJoinRequestData;
|
||||
import org.qora.data.group.GroupMemberData;
|
||||
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
||||
import org.qora.data.transaction.CancelGroupInviteTransactionData;
|
||||
import org.qora.data.transaction.CreateGroupTransactionData;
|
||||
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||
import org.qora.data.transaction.GroupKickTransactionData;
|
||||
import org.qora.data.transaction.JoinGroupTransactionData;
|
||||
import org.qora.data.transaction.LeaveGroupTransactionData;
|
||||
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
||||
@ -43,7 +48,10 @@ import org.qora.transaction.Transaction;
|
||||
import org.qora.transaction.Transaction.ValidationResult;
|
||||
import org.qora.transform.TransformationException;
|
||||
import org.qora.transform.transaction.AddGroupAdminTransactionTransformer;
|
||||
import org.qora.transform.transaction.CancelGroupInviteTransactionTransformer;
|
||||
import org.qora.transform.transaction.CreateGroupTransactionTransformer;
|
||||
import org.qora.transform.transaction.GroupInviteTransactionTransformer;
|
||||
import org.qora.transform.transaction.GroupKickTransactionTransformer;
|
||||
import org.qora.transform.transaction.JoinGroupTransactionTransformer;
|
||||
import org.qora.transform.transaction.LeaveGroupTransactionTransformer;
|
||||
import org.qora.transform.transaction.RemoveGroupAdminTransactionTransformer;
|
||||
@ -140,7 +148,7 @@ public class GroupsResource {
|
||||
Integer memberCount = null;
|
||||
|
||||
if (includeMembers) {
|
||||
groupMembers = repository.getGroupRepository().getAllGroupMembers(groupData.getGroupName());
|
||||
groupMembers = repository.getGroupRepository().getGroupMembers(groupData.getGroupName());
|
||||
|
||||
// Strip groupName from member info
|
||||
groupMembers = groupMembers.stream().map(groupMemberData -> new GroupMemberData(null, groupMemberData.getMember(), groupMemberData.getJoined(), null)).collect(Collectors.toList());
|
||||
@ -152,7 +160,7 @@ public class GroupsResource {
|
||||
}
|
||||
|
||||
// Always include admins
|
||||
List<GroupAdminData> groupAdmins = repository.getGroupRepository().getAllGroupAdmins(groupData.getGroupName());
|
||||
List<GroupAdminData> groupAdmins = repository.getGroupRepository().getGroupAdmins(groupData.getGroupName());
|
||||
|
||||
// We only need admin addresses
|
||||
List<String> groupAdminAddresses = groupAdmins.stream().map(groupAdminData -> groupAdminData.getAdmin()).collect(Collectors.toList());
|
||||
@ -336,6 +344,135 @@ public class GroupsResource {
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/kick")
|
||||
@Operation(
|
||||
summary = "Build raw, unsigned, GROUP_KICK transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = GroupKickTransactionData.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, unsigned, GROUP_KICK transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String groupKick(GroupKickTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
ValidationResult result = transaction.isValidUnconfirmed();
|
||||
if (result != ValidationResult.OK)
|
||||
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||
|
||||
byte[] bytes = GroupKickTransactionTransformer.toBytes(transactionData);
|
||||
return Base58.encode(bytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/invite")
|
||||
@Operation(
|
||||
summary = "Build raw, unsigned, GROUP_INVITE transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = GroupInviteTransactionData.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, unsigned, GROUP_INVITE transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String groupInvite(GroupInviteTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
ValidationResult result = transaction.isValidUnconfirmed();
|
||||
if (result != ValidationResult.OK)
|
||||
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||
|
||||
byte[] bytes = GroupInviteTransactionTransformer.toBytes(transactionData);
|
||||
return Base58.encode(bytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/invite/cancel")
|
||||
@Operation(
|
||||
summary = "Build raw, unsigned, CANCEL_GROUP_INVITE transaction",
|
||||
requestBody = @RequestBody(
|
||||
required = true,
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(
|
||||
implementation = CancelGroupInviteTransactionData.class
|
||||
)
|
||||
)
|
||||
),
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "raw, unsigned, CANCEL_GROUP_INVITE transaction encoded in Base58",
|
||||
content = @Content(
|
||||
mediaType = MediaType.TEXT_PLAIN,
|
||||
schema = @Schema(
|
||||
type = "string"
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.TRANSACTION_INVALID, ApiError.TRANSFORMATION_ERROR, ApiError.REPOSITORY_ISSUE})
|
||||
public String cancelGroupInvite(CancelGroupInviteTransactionData transactionData) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Transaction transaction = Transaction.fromData(repository, transactionData);
|
||||
|
||||
ValidationResult result = transaction.isValidUnconfirmed();
|
||||
if (result != ValidationResult.OK)
|
||||
throw TransactionsResource.createTransactionInvalidException(request, result);
|
||||
|
||||
byte[] bytes = CancelGroupInviteTransactionTransformer.toBytes(transactionData);
|
||||
return Base58.encode(bytes);
|
||||
} catch (TransformationException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSFORMATION_ERROR, e);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/join")
|
||||
@Operation(
|
||||
@ -422,4 +559,50 @@ public class GroupsResource {
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/invites/{groupname}")
|
||||
@Operation(
|
||||
summary = "Pending group invites",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "group invite",
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = GroupInviteData.class)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public List<GroupInviteData> getInvites(@PathParam("groupname") String groupName) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
return repository.getGroupRepository().getGroupInvites(groupName);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/joinrequests/{groupname}")
|
||||
@Operation(
|
||||
summary = "Pending group join requests",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
description = "group jon requests",
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = GroupJoinRequestData.class)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public List<GroupJoinRequestData> getJoinRequests(@PathParam("groupname") String groupName) {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
return repository.getGroupRepository().getGroupJoinRequests(groupName);
|
||||
} catch (DataException e) {
|
||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
60
src/main/java/org/qora/data/group/GroupInviteData.java
Normal file
60
src/main/java/org/qora/data/group/GroupInviteData.java
Normal file
@ -0,0 +1,60 @@
|
||||
package org.qora.data.group;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class GroupInviteData {
|
||||
|
||||
// Properties
|
||||
private String groupName;
|
||||
private String inviter;
|
||||
private String invitee;
|
||||
private Long expiry;
|
||||
// No need to ever expose this via API
|
||||
@XmlTransient
|
||||
private byte[] reference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
protected GroupInviteData() {
|
||||
}
|
||||
|
||||
public GroupInviteData(String groupName, String inviter, String invitee, Long expiry, byte[] reference) {
|
||||
this.groupName = groupName;
|
||||
this.inviter = inviter;
|
||||
this.invitee = invitee;
|
||||
this.expiry = expiry;
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public String getInviter() {
|
||||
return this.inviter;
|
||||
}
|
||||
|
||||
public String getInvitee() {
|
||||
return this.invitee;
|
||||
}
|
||||
|
||||
public Long getExpiry() {
|
||||
return this.expiry;
|
||||
}
|
||||
|
||||
public byte[] getReference() {
|
||||
return this.reference;
|
||||
}
|
||||
|
||||
public void setReference(byte[] reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
|
||||
}
|
35
src/main/java/org/qora/data/group/GroupJoinRequestData.java
Normal file
35
src/main/java/org/qora/data/group/GroupJoinRequestData.java
Normal file
@ -0,0 +1,35 @@
|
||||
package org.qora.data.group;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class GroupJoinRequestData {
|
||||
|
||||
// Properties
|
||||
private String groupName;
|
||||
private String joiner;
|
||||
|
||||
// Constructors
|
||||
|
||||
// necessary for JAX-RS serialization
|
||||
protected GroupJoinRequestData() {
|
||||
}
|
||||
|
||||
public GroupJoinRequestData(String groupName, String joiner) {
|
||||
this.groupName = groupName;
|
||||
this.joiner = joiner;
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public String getJoiner() {
|
||||
return this.joiner;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,84 @@
|
||||
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.XmlTransient;
|
||||
|
||||
import org.qora.transaction.Transaction.TransactionType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class CancelGroupInviteTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
@Schema(description = "admin's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||
private byte[] adminPublicKey;
|
||||
@Schema(description = "group name", example = "my-group")
|
||||
private String groupName;
|
||||
@Schema(description = "invitee's address", example = "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK")
|
||||
private String invitee;
|
||||
// No need to ever expose this via API
|
||||
@XmlTransient
|
||||
private byte[] groupReference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected CancelGroupInviteTransactionData() {
|
||||
super(TransactionType.CANCEL_GROUP_INVITE);
|
||||
}
|
||||
|
||||
public void afterUnmarshal(Unmarshaller u, Object parent) {
|
||||
this.creatorPublicKey = this.adminPublicKey;
|
||||
}
|
||||
|
||||
public CancelGroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, byte[] groupReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.CANCEL_GROUP_INVITE, fee, adminPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.adminPublicKey = adminPublicKey;
|
||||
this.groupName = groupName;
|
||||
this.invitee = invitee;
|
||||
this.groupReference = groupReference;
|
||||
}
|
||||
|
||||
public CancelGroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, byte[] groupReference, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(adminPublicKey, groupName, invitee, groupReference, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
public CancelGroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
this(adminPublicKey, groupName, invitee, null, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public CancelGroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(adminPublicKey, groupName, invitee, null, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getAdminPublicKey() {
|
||||
return this.adminPublicKey;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public String getInvitee() {
|
||||
return this.invitee;
|
||||
}
|
||||
|
||||
public byte[] getGroupReference() {
|
||||
return this.groupReference;
|
||||
}
|
||||
|
||||
public void setGroupReference(byte[] groupReference) {
|
||||
this.groupReference = groupReference;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
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.XmlTransient;
|
||||
|
||||
import org.qora.transaction.Transaction.TransactionType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class GroupInviteTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
@Schema(description = "admin's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||
private byte[] adminPublicKey;
|
||||
@Schema(description = "group name", example = "my-group")
|
||||
private String groupName;
|
||||
@Schema(description = "invitee's address", example = "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK")
|
||||
private String invitee;
|
||||
@Schema(description = "invitation lifetime in seconds")
|
||||
private int timeToLive;
|
||||
// No need to ever expose this via API
|
||||
@XmlTransient
|
||||
private byte[] groupReference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected GroupInviteTransactionData() {
|
||||
super(TransactionType.GROUP_INVITE);
|
||||
}
|
||||
|
||||
public void afterUnmarshal(Unmarshaller u, Object parent) {
|
||||
this.creatorPublicKey = this.adminPublicKey;
|
||||
}
|
||||
|
||||
public GroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, int timeToLive, byte[] groupReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.GROUP_INVITE, fee, adminPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.adminPublicKey = adminPublicKey;
|
||||
this.groupName = groupName;
|
||||
this.invitee = invitee;
|
||||
this.timeToLive = timeToLive;
|
||||
this.groupReference = groupReference;
|
||||
}
|
||||
|
||||
public GroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, int timeToLive, byte[] groupReference, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(adminPublicKey, groupName, invitee, timeToLive, groupReference, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
public GroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, int timeToLive, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
this(adminPublicKey, groupName, invitee, timeToLive, null, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public GroupInviteTransactionData(byte[] adminPublicKey, String groupName, String invitee, int timeToLive, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(adminPublicKey, groupName, invitee, timeToLive, null, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getAdminPublicKey() {
|
||||
return this.adminPublicKey;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public String getInvitee() {
|
||||
return this.invitee;
|
||||
}
|
||||
|
||||
public int getTimeToLive() {
|
||||
return this.timeToLive;
|
||||
}
|
||||
|
||||
public byte[] getGroupReference() {
|
||||
return this.groupReference;
|
||||
}
|
||||
|
||||
public void setGroupReference(byte[] groupReference) {
|
||||
this.groupReference = groupReference;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
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.XmlTransient;
|
||||
|
||||
import org.qora.transaction.Transaction.TransactionType;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
// All properties to be converted to JSON via JAX-RS
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
@Schema(allOf = { TransactionData.class })
|
||||
public class GroupKickTransactionData extends TransactionData {
|
||||
|
||||
// Properties
|
||||
@Schema(description = "admin's public key", example = "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP")
|
||||
private byte[] adminPublicKey;
|
||||
@Schema(description = "group name", example = "my-group")
|
||||
private String groupName;
|
||||
@Schema(description = "member to kick from group", example = "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK")
|
||||
private String member;
|
||||
@Schema(description = "reason for kick")
|
||||
private String reason;
|
||||
// No need to ever expose this via API
|
||||
@XmlTransient
|
||||
private byte[] memberReference;
|
||||
// No need to ever expose this via API
|
||||
@XmlTransient
|
||||
private byte[] adminReference;
|
||||
|
||||
// Constructors
|
||||
|
||||
// For JAX-RS
|
||||
protected GroupKickTransactionData() {
|
||||
super(TransactionType.GROUP_KICK);
|
||||
}
|
||||
|
||||
public void afterUnmarshal(Unmarshaller u, Object parent) {
|
||||
this.creatorPublicKey = this.adminPublicKey;
|
||||
}
|
||||
|
||||
public GroupKickTransactionData(byte[] adminPublicKey, String groupName, String member, String reason, byte[] memberReference, byte[] adminReference, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
super(TransactionType.GROUP_KICK, fee, adminPublicKey, timestamp, reference, signature);
|
||||
|
||||
this.adminPublicKey = adminPublicKey;
|
||||
this.groupName = groupName;
|
||||
this.member = member;
|
||||
this.reason = reason;
|
||||
this.memberReference = memberReference;
|
||||
this.adminReference = adminReference;
|
||||
}
|
||||
|
||||
public GroupKickTransactionData(byte[] adminPublicKey, String groupName, String member, String reason, BigDecimal fee, long timestamp, byte[] reference, byte[] signature) {
|
||||
this(adminPublicKey, groupName, member, reason, null, null, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public GroupKickTransactionData(byte[] adminPublicKey, String groupName, String member, String reason, byte[] memberReference, byte[] adminReference, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(adminPublicKey, groupName, member, reason, memberReference, adminReference, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
public GroupKickTransactionData(byte[] adminPublicKey, String groupName, String member, String reason, BigDecimal fee, long timestamp, byte[] reference) {
|
||||
this(adminPublicKey, groupName, member, reason, null, null, fee, timestamp, reference, null);
|
||||
}
|
||||
|
||||
// Getters / setters
|
||||
|
||||
public byte[] getAdminPublicKey() {
|
||||
return this.adminPublicKey;
|
||||
}
|
||||
|
||||
public String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
public String getMember() {
|
||||
return this.member;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return this.reason;
|
||||
}
|
||||
|
||||
public byte[] getMemberReference() {
|
||||
return this.memberReference;
|
||||
}
|
||||
|
||||
public void setMemberReference(byte[] memberReference) {
|
||||
this.memberReference = memberReference;
|
||||
}
|
||||
|
||||
public byte[] getAdminReference() {
|
||||
return this.adminReference;
|
||||
}
|
||||
|
||||
public void setAdminReference(byte[] adminReference) {
|
||||
this.adminReference = adminReference;
|
||||
}
|
||||
|
||||
}
|
@ -35,6 +35,7 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
|
||||
MultiPaymentTransactionData.class, DeployATTransactionData.class, MessageTransactionData.class, ATTransactionData.class,
|
||||
CreateGroupTransactionData.class, UpdateGroupTransactionData.class,
|
||||
AddGroupAdminTransactionData.class, RemoveGroupAdminTransactionData.class,
|
||||
GroupKickTransactionData.class, GroupInviteTransactionData.class,
|
||||
JoinGroupTransactionData.class, LeaveGroupTransactionData.class
|
||||
})
|
||||
//All properties to be converted to JSON via JAX-RS
|
||||
|
@ -1,14 +1,20 @@
|
||||
package org.qora.group;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.qora.account.Account;
|
||||
import org.qora.account.PublicKeyAccount;
|
||||
import org.qora.data.group.GroupAdminData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.group.GroupInviteData;
|
||||
import org.qora.data.group.GroupJoinRequestData;
|
||||
import org.qora.data.group.GroupMemberData;
|
||||
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
||||
import org.qora.data.transaction.CancelGroupInviteTransactionData;
|
||||
import org.qora.data.transaction.CreateGroupTransactionData;
|
||||
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||
import org.qora.data.transaction.GroupKickTransactionData;
|
||||
import org.qora.data.transaction.JoinGroupTransactionData;
|
||||
import org.qora.data.transaction.LeaveGroupTransactionData;
|
||||
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
||||
@ -27,6 +33,8 @@ public class Group {
|
||||
// Useful constants
|
||||
public static final int MAX_NAME_SIZE = 400;
|
||||
public static final int MAX_DESCRIPTION_SIZE = 4000;
|
||||
/** Max size of kick/ban reason */
|
||||
public static final int MAX_REASON_SIZE = 400;
|
||||
|
||||
// Constructors
|
||||
|
||||
@ -38,9 +46,9 @@ public class Group {
|
||||
*/
|
||||
public Group(Repository repository, CreateGroupTransactionData createGroupTransactionData) {
|
||||
this.repository = repository;
|
||||
this.groupData = new GroupData(createGroupTransactionData.getOwner(),
|
||||
createGroupTransactionData.getGroupName(), createGroupTransactionData.getDescription(), createGroupTransactionData.getTimestamp(),
|
||||
createGroupTransactionData.getIsOpen(), createGroupTransactionData.getSignature());
|
||||
this.groupData = new GroupData(createGroupTransactionData.getOwner(), createGroupTransactionData.getGroupName(),
|
||||
createGroupTransactionData.getDescription(), createGroupTransactionData.getTimestamp(), createGroupTransactionData.getIsOpen(),
|
||||
createGroupTransactionData.getSignature());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -61,10 +69,12 @@ public class Group {
|
||||
this.repository.getGroupRepository().save(this.groupData);
|
||||
|
||||
// Add owner as admin too
|
||||
this.repository.getGroupRepository().save(new GroupAdminData(this.groupData.getGroupName(), this.groupData.getOwner(), createGroupTransactionData.getSignature()));
|
||||
this.repository.getGroupRepository()
|
||||
.save(new GroupAdminData(this.groupData.getGroupName(), this.groupData.getOwner(), createGroupTransactionData.getSignature()));
|
||||
|
||||
// Add owner as member too
|
||||
this.repository.getGroupRepository().save(new GroupMemberData(this.groupData.getGroupName(), this.groupData.getOwner(), this.groupData.getCreated(), createGroupTransactionData.getSignature()));
|
||||
this.repository.getGroupRepository().save(new GroupMemberData(this.groupData.getGroupName(), this.groupData.getOwner(), this.groupData.getCreated(),
|
||||
createGroupTransactionData.getSignature()));
|
||||
}
|
||||
|
||||
public void uncreate() throws DataException {
|
||||
@ -124,7 +134,8 @@ public class Group {
|
||||
|
||||
// New owner should be a member if not already
|
||||
if (!groupRepository.memberExists(groupName, newOwner)) {
|
||||
GroupMemberData groupMemberData = new GroupMemberData(groupName, newOwner, updateGroupTransactionData.getTimestamp(), updateGroupTransactionData.getSignature());
|
||||
GroupMemberData groupMemberData = new GroupMemberData(groupName, newOwner, updateGroupTransactionData.getTimestamp(),
|
||||
updateGroupTransactionData.getSignature());
|
||||
groupRepository.save(groupMemberData);
|
||||
}
|
||||
|
||||
@ -168,7 +179,8 @@ public class Group {
|
||||
}
|
||||
|
||||
public void promoteToAdmin(AddGroupAdminTransactionData addGroupAdminTransactionData) throws DataException {
|
||||
GroupAdminData groupAdminData = new GroupAdminData(addGroupAdminTransactionData.getGroupName(), addGroupAdminTransactionData.getMember(), addGroupAdminTransactionData.getSignature());
|
||||
GroupAdminData groupAdminData = new GroupAdminData(addGroupAdminTransactionData.getGroupName(), addGroupAdminTransactionData.getMember(),
|
||||
addGroupAdminTransactionData.getSignature());
|
||||
this.repository.getGroupRepository().save(groupAdminData);
|
||||
}
|
||||
|
||||
@ -199,17 +211,160 @@ public class Group {
|
||||
groupRepository.save(groupAdminData);
|
||||
}
|
||||
|
||||
public void kick(GroupKickTransactionData groupKickTransactionData) throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = groupKickTransactionData.getGroupName();
|
||||
String member = groupKickTransactionData.getMember();
|
||||
|
||||
// Store membership and (optionally) adminship transactions for orphaning purposes
|
||||
GroupAdminData groupAdminData = groupRepository.getAdmin(groupName, member);
|
||||
if (groupAdminData != null) {
|
||||
groupKickTransactionData.setAdminReference(groupAdminData.getGroupReference());
|
||||
|
||||
groupRepository.deleteAdmin(groupName, member);
|
||||
}
|
||||
|
||||
GroupMemberData groupMemberData = groupRepository.getMember(groupName, member);
|
||||
groupKickTransactionData.setMemberReference(groupMemberData.getGroupReference());
|
||||
|
||||
groupRepository.deleteMember(groupName, member);
|
||||
}
|
||||
|
||||
public void unkick(GroupKickTransactionData groupKickTransactionData) throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = groupKickTransactionData.getGroupName();
|
||||
String member = groupKickTransactionData.getMember();
|
||||
|
||||
// Rebuild member entry using stored transaction reference
|
||||
TransactionData membershipTransactionData = this.repository.getTransactionRepository().fromSignature(groupKickTransactionData.getMemberReference());
|
||||
GroupMemberData groupMemberData = new GroupMemberData(groupName, member, membershipTransactionData.getTimestamp(),
|
||||
membershipTransactionData.getSignature());
|
||||
groupRepository.save(groupMemberData);
|
||||
|
||||
if (groupKickTransactionData.getAdminReference() != null) {
|
||||
// Rebuild admin entry using stored transaction reference
|
||||
GroupAdminData groupAdminData = new GroupAdminData(groupName, member, groupKickTransactionData.getAdminReference());
|
||||
groupRepository.save(groupAdminData);
|
||||
}
|
||||
}
|
||||
|
||||
public void invite(GroupInviteTransactionData groupInviteTransactionData) throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = groupInviteTransactionData.getGroupName();
|
||||
Account inviter = new PublicKeyAccount(this.repository, groupInviteTransactionData.getAdminPublicKey());
|
||||
|
||||
// If there is a pending "join request" then add new group member
|
||||
if (groupRepository.joinRequestExists(groupName, groupInviteTransactionData.getInvitee())) {
|
||||
GroupMemberData groupMemberData = new GroupMemberData(groupName, groupInviteTransactionData.getInvitee(), groupInviteTransactionData.getTimestamp(),
|
||||
groupInviteTransactionData.getSignature());
|
||||
groupRepository.save(groupMemberData);
|
||||
|
||||
// Delete join request
|
||||
groupRepository.deleteJoinRequest(groupName, groupInviteTransactionData.getInvitee());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
Long expiry;
|
||||
int timeToLive = groupInviteTransactionData.getTimeToLive();
|
||||
if (timeToLive == 0)
|
||||
expiry = null;
|
||||
else
|
||||
expiry = groupInviteTransactionData.getTimestamp() + timeToLive;
|
||||
|
||||
GroupInviteData groupInviteData = new GroupInviteData(groupName, inviter.getAddress(), groupInviteTransactionData.getInvitee(), expiry,
|
||||
groupInviteTransactionData.getSignature());
|
||||
groupRepository.save(groupInviteData);
|
||||
}
|
||||
|
||||
public void uninvite(GroupInviteTransactionData groupInviteTransactionData) throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = groupInviteTransactionData.getGroupName();
|
||||
Account inviter = new PublicKeyAccount(this.repository, groupInviteTransactionData.getAdminPublicKey());
|
||||
String invitee = groupInviteTransactionData.getInvitee();
|
||||
|
||||
// Put back any "join request"
|
||||
if (groupRepository.memberExists(groupName, invitee)) {
|
||||
GroupJoinRequestData groupJoinRequestData = new GroupJoinRequestData(groupName, invitee);
|
||||
groupRepository.save(groupJoinRequestData);
|
||||
|
||||
// Delete member
|
||||
groupRepository.deleteMember(groupName, invitee);
|
||||
}
|
||||
|
||||
// Delete invite
|
||||
groupRepository.deleteInvite(groupName, inviter.getAddress(), invitee);
|
||||
}
|
||||
|
||||
public void cancelInvite(CancelGroupInviteTransactionData cancelGroupInviteTransactionData) throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = cancelGroupInviteTransactionData.getGroupName();
|
||||
Account inviter = new PublicKeyAccount(this.repository, cancelGroupInviteTransactionData.getAdminPublicKey());
|
||||
String invitee = cancelGroupInviteTransactionData.getInvitee();
|
||||
|
||||
// Save invite's transaction signature for orphaning purposes
|
||||
GroupInviteData groupInviteData = groupRepository.getInvite(groupName, inviter.getAddress(), invitee);
|
||||
cancelGroupInviteTransactionData.setGroupReference(groupInviteData.getReference());
|
||||
|
||||
// Delete invite
|
||||
groupRepository.deleteInvite(groupName, inviter.getAddress(), invitee);
|
||||
}
|
||||
|
||||
public void uncancelInvite(CancelGroupInviteTransactionData cancelGroupInviteTransactionData) throws DataException {
|
||||
// Reinstate invite
|
||||
TransactionData transactionData = this.repository.getTransactionRepository().fromSignature(cancelGroupInviteTransactionData.getGroupReference());
|
||||
invite((GroupInviteTransactionData) transactionData);
|
||||
|
||||
cancelGroupInviteTransactionData.setGroupReference(null);
|
||||
}
|
||||
|
||||
public void join(JoinGroupTransactionData joinGroupTransactionData) throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = joinGroupTransactionData.getGroupName();
|
||||
Account joiner = new PublicKeyAccount(this.repository, joinGroupTransactionData.getJoinerPublicKey());
|
||||
|
||||
GroupMemberData groupMemberData = new GroupMemberData(joinGroupTransactionData.getGroupName(), joiner.getAddress(), joinGroupTransactionData.getTimestamp(), joinGroupTransactionData.getSignature());
|
||||
this.repository.getGroupRepository().save(groupMemberData);
|
||||
// Set invite transactions' group-reference to this transaction's signature so the invites can be put back if we orphan this join
|
||||
// Delete any pending invites
|
||||
List<GroupInviteData> invites = groupRepository.getInvitesByInvitee(groupName, joiner.getAddress());
|
||||
for (GroupInviteData invite : invites) {
|
||||
TransactionData transactionData = this.repository.getTransactionRepository().fromSignature(invite.getReference());
|
||||
((GroupInviteTransactionData) transactionData).setGroupReference(joinGroupTransactionData.getSignature());
|
||||
|
||||
groupRepository.deleteInvite(groupName, invite.getInviter(), joiner.getAddress());
|
||||
}
|
||||
|
||||
// If there were no invites and this group is "closed" (i.e. invite-only) then
|
||||
// this is now a pending "join request"
|
||||
if (invites.isEmpty() && !groupData.getIsOpen()) {
|
||||
GroupJoinRequestData groupJoinRequestData = new GroupJoinRequestData(groupName, joiner.getAddress());
|
||||
groupRepository.save(groupJoinRequestData);
|
||||
return;
|
||||
}
|
||||
|
||||
// Actually add new member to group
|
||||
GroupMemberData groupMemberData = new GroupMemberData(groupName, joiner.getAddress(), joinGroupTransactionData.getTimestamp(),
|
||||
joinGroupTransactionData.getSignature());
|
||||
groupRepository.save(groupMemberData);
|
||||
|
||||
}
|
||||
|
||||
public void unjoin(JoinGroupTransactionData joinGroupTransactionData) throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = joinGroupTransactionData.getGroupName();
|
||||
Account joiner = new PublicKeyAccount(this.repository, joinGroupTransactionData.getJoinerPublicKey());
|
||||
|
||||
this.repository.getGroupRepository().deleteMember(joinGroupTransactionData.getGroupName(), joiner.getAddress());
|
||||
groupRepository.deleteMember(groupName, joiner.getAddress());
|
||||
|
||||
// Put back any pending invites
|
||||
List<GroupInviteTransactionData> inviteTransactions = this.repository.getTransactionRepository()
|
||||
.getInvitesWithGroupReference(joinGroupTransactionData.getSignature());
|
||||
for (GroupInviteTransactionData inviteTransaction : inviteTransactions) {
|
||||
this.invite(inviteTransaction);
|
||||
|
||||
// Remove group-reference
|
||||
inviteTransaction.setGroupReference(null);
|
||||
this.repository.getTransactionRepository().save(inviteTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
public void leave(LeaveGroupTransactionData leaveGroupTransactionData) throws DataException {
|
||||
@ -249,7 +404,8 @@ public class Group {
|
||||
|
||||
// Rejoin as member
|
||||
TransactionData membershipTransactionData = this.repository.getTransactionRepository().fromSignature(leaveGroupTransactionData.getMemberReference());
|
||||
groupRepository.save(new GroupMemberData(groupName, leaver.getAddress(), membershipTransactionData.getTimestamp(), membershipTransactionData.getSignature()));
|
||||
groupRepository
|
||||
.save(new GroupMemberData(groupName, leaver.getAddress(), membershipTransactionData.getTimestamp(), membershipTransactionData.getSignature()));
|
||||
|
||||
// Put back any admin state based on referenced group-related transaction
|
||||
byte[] adminTransactionSignature = leaveGroupTransactionData.getAdminReference();
|
||||
|
@ -4,6 +4,8 @@ import java.util.List;
|
||||
|
||||
import org.qora.data.group.GroupAdminData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.group.GroupInviteData;
|
||||
import org.qora.data.group.GroupJoinRequestData;
|
||||
import org.qora.data.group.GroupMemberData;
|
||||
|
||||
public interface GroupRepository {
|
||||
@ -28,7 +30,7 @@ public interface GroupRepository {
|
||||
|
||||
public boolean adminExists(String groupName, String address) throws DataException;
|
||||
|
||||
public List<GroupAdminData> getAllGroupAdmins(String groupName) throws DataException;
|
||||
public List<GroupAdminData> getGroupAdmins(String groupName) throws DataException;
|
||||
|
||||
public void save(GroupAdminData groupAdminData) throws DataException;
|
||||
|
||||
@ -40,7 +42,7 @@ public interface GroupRepository {
|
||||
|
||||
public boolean memberExists(String groupName, String address) throws DataException;
|
||||
|
||||
public List<GroupMemberData> getAllGroupMembers(String groupName) throws DataException;
|
||||
public List<GroupMemberData> getGroupMembers(String groupName) throws DataException;
|
||||
|
||||
/** Returns number of group members, or null if group doesn't exist */
|
||||
public Integer countGroupMembers(String groupName) throws DataException;
|
||||
@ -49,4 +51,30 @@ public interface GroupRepository {
|
||||
|
||||
public void deleteMember(String groupName, String address) throws DataException;
|
||||
|
||||
// Group Invites
|
||||
|
||||
public GroupInviteData getInvite(String groupName, String inviter, String invitee) throws DataException;
|
||||
|
||||
public boolean hasInvite(String groupName, String invitee) throws DataException;
|
||||
|
||||
public boolean inviteExists(String groupName, String inviter, String invitee) throws DataException;
|
||||
|
||||
public List<GroupInviteData> getGroupInvites(String groupName) throws DataException;
|
||||
|
||||
public List<GroupInviteData> getInvitesByInvitee(String groupName, String invitee) throws DataException;
|
||||
|
||||
public void save(GroupInviteData groupInviteData) throws DataException;
|
||||
|
||||
public void deleteInvite(String groupName, String inviter, String invitee) throws DataException;
|
||||
|
||||
// Group Join Requests
|
||||
|
||||
public boolean joinRequestExists(String groupName, String joiner) throws DataException;
|
||||
|
||||
public List<GroupJoinRequestData> getGroupJoinRequests(String groupName) throws DataException;
|
||||
|
||||
public void save(GroupJoinRequestData groupJoinRequestData) throws DataException;
|
||||
|
||||
public void deleteJoinRequest(String groupName, String joiner) throws DataException;
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package org.qora.repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.transaction.Transaction.TransactionType;
|
||||
|
||||
@ -52,4 +53,8 @@ public interface TransactionRepository {
|
||||
|
||||
public void delete(TransactionData transactionData) throws DataException;
|
||||
|
||||
/** Returns transaction data for group invite transactions that have groupReference that matches passed value.
|
||||
* @throws DataException */
|
||||
public List<GroupInviteTransactionData> getInvitesWithGroupReference(byte[] groupReference) throws DataException;
|
||||
|
||||
}
|
||||
|
@ -393,57 +393,48 @@ public class HSQLDBDatabaseUpdates {
|
||||
break;
|
||||
|
||||
case 28:
|
||||
// XXX TEMP fixes to registered names - remove before database rebuild!
|
||||
// Allow name_reference to be NULL while transaction is unconfirmed
|
||||
stmt.execute("ALTER TABLE UpdateNameTransactions ALTER COLUMN name_reference SET NULL");
|
||||
stmt.execute("ALTER TABLE BuyNameTransactions ALTER COLUMN name_reference SET NULL");
|
||||
// Names.registrant shouldn't be there
|
||||
stmt.execute("ALTER TABLE Names DROP COLUMN registrant");
|
||||
break;
|
||||
|
||||
case 29:
|
||||
// XXX TEMP bridging statements for AccountGroups - remove before database rebuild!
|
||||
stmt.execute("CREATE TYPE GenericDescription AS VARCHAR(4000)");
|
||||
stmt.execute("CREATE TYPE GroupName AS VARCHAR(400) COLLATE SQL_TEXT_UCC_NO_PAD");
|
||||
break;
|
||||
|
||||
case 30:
|
||||
// Account groups
|
||||
stmt.execute("CREATE TABLE AccountGroups (group_name GroupName, owner QoraAddress NOT NULL, description GenericDescription NOT NULL, "
|
||||
+ "created TIMESTAMP WITH TIME ZONE NOT NULL, updated TIMESTAMP WITH TIME ZONE, is_open BOOLEAN NOT NULL, "
|
||||
+ "reference Signature, PRIMARY KEY (group_name))");
|
||||
// For finding groups by owner
|
||||
stmt.execute("CREATE INDEX AccountGroupOwnerIndex on AccountGroups (owner)");
|
||||
stmt.execute("CREATE INDEX AccountGroupOwnerIndex ON AccountGroups (owner)");
|
||||
|
||||
// Admins
|
||||
stmt.execute("CREATE TABLE AccountGroupAdmins (group_name GroupName, admin QoraAddress, group_reference Signature NOT NULL, PRIMARY KEY (group_name, admin))");
|
||||
// For finding groups that address administrates
|
||||
stmt.execute("CREATE INDEX AccountGroupAdminIndex on AccountGroupAdmins (admin)");
|
||||
stmt.execute("CREATE INDEX AccountGroupAdminIndex ON AccountGroupAdmins (admin)");
|
||||
|
||||
// Members
|
||||
stmt.execute("CREATE TABLE AccountGroupMembers (group_name GroupName, address QoraAddress, joined TIMESTAMP WITH TIME ZONE NOT NULL, group_reference Signature NOT NULL, "
|
||||
+ "PRIMARY KEY (group_name, address))");
|
||||
// For finding groups that address is member
|
||||
stmt.execute("CREATE INDEX AccountGroupMemberIndex on AccountGroupMembers (address)");
|
||||
stmt.execute("CREATE INDEX AccountGroupMemberIndex ON AccountGroupMembers (address)");
|
||||
|
||||
// Invites
|
||||
// PRIMARY KEY (invitee + group + inviter) because most queries will be "what have I been invited to?" from UI
|
||||
stmt.execute("CREATE TABLE AccountGroupInvites (group_name GroupName, invitee QoraAddress, inviter QoraAddress, "
|
||||
+ "expiry TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY (invitee, group_name, inviter))");
|
||||
stmt.execute("CREATE TABLE AccountGroupInvites (group_name GroupName, inviter QoraAddress, invitee QoraAddress, "
|
||||
+ "expiry TIMESTAMP WITH TIME ZONE NOT NULL, reference Signature, PRIMARY KEY (invitee, group_name, inviter))");
|
||||
// For finding invites sent by inviter
|
||||
stmt.execute("CREATE INDEX AccountGroupSentInviteIndex on AccountGroupInvites (inviter)");
|
||||
stmt.execute("CREATE INDEX AccountGroupSentInviteIndex ON AccountGroupInvites (inviter)");
|
||||
// For finding invites by group
|
||||
stmt.execute("CREATE INDEX AccountGroupInviteIndex on AccountGroupInvites (group_name)");
|
||||
stmt.execute("CREATE INDEX AccountGroupInviteIndex ON AccountGroupInvites (group_name)");
|
||||
// For expiry maintenance
|
||||
stmt.execute("CREATE INDEX AccountGroupInviteExpiryIndex ON AccountGroupInvites (expiry)");
|
||||
|
||||
// Pending "join requests"
|
||||
stmt.execute("CREATE TABLE AccountGroupJoinRequests (group_name GroupName, joiner QoraAddress, "
|
||||
+ "PRIMARY KEY (group_name, joiner))");
|
||||
|
||||
// Bans
|
||||
// NULL expiry means does not expire!
|
||||
stmt.execute("CREATE TABLE AccountGroupBans (group_name GroupName, offender QoraAddress, admin QoraAddress NOT NULL, banned TIMESTAMP WITH TIME ZONE NOT NULL, "
|
||||
+ "reason GenericDescription NOT NULL, expiry TIMESTAMP WITH TIME ZONE, PRIMARY KEY (group_name, offender))");
|
||||
// For expiry maintenance
|
||||
stmt.execute("CREATE INDEX AccountGroupBanExpiryIndex on AccountGroupBans (expiry)");
|
||||
stmt.execute("CREATE INDEX AccountGroupBanExpiryIndex ON AccountGroupBans (expiry)");
|
||||
break;
|
||||
|
||||
case 31:
|
||||
case 29:
|
||||
// Account group transactions
|
||||
stmt.execute("CREATE TABLE CreateGroupTransactions (signature Signature, creator QoraPublicKey NOT NULL, group_name GroupName NOT NULL, "
|
||||
+ "owner QoraAddress NOT NULL, description GenericDescription NOT NULL, is_open BOOLEAN NOT NULL, "
|
||||
@ -464,6 +455,21 @@ public class HSQLDBDatabaseUpdates {
|
||||
stmt.execute("CREATE TABLE LeaveGroupTransactions (signature Signature, leaver QoraPublicKey NOT NULL, group_name GroupName NOT NULL, "
|
||||
+ "member_reference Signature, admin_reference Signature, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
|
||||
// Account group kick transaction
|
||||
stmt.execute("CREATE TABLE GroupKickTransactions (signature Signature, admin QoraPublicKey NOT NULL, group_name GroupName NOT NULL, address QoraAddress NOT NULL, "
|
||||
+ "reason VARCHAR(400), member_reference Signature, admin_reference Signature, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
|
||||
// Account group invite/cancel-invite transactions
|
||||
stmt.execute("CREATE TABLE GroupInviteTransactions (signature Signature, admin QoraPublicKey NOT NULL, group_name GroupName NOT NULL, invitee QoraAddress NOT NULL, "
|
||||
+ "time_to_live INTEGER NOT NULL, group_reference Signature, "
|
||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
// For finding invite transactions during orphaning
|
||||
stmt.execute("CREATE INDEX GroupInviteTransactionReferenceIndex ON GroupInviteTransactions (group_reference)");
|
||||
// Cancel group invite
|
||||
stmt.execute("CREATE TABLE CancelGroupInviteTransactions (signature Signature, admin QoraPublicKey NOT NULL, group_name GroupName NOT NULL, invitee QoraAddress NOT NULL, "
|
||||
+ "group_reference Signature, PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -9,6 +9,8 @@ import java.util.List;
|
||||
|
||||
import org.qora.data.group.GroupAdminData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.group.GroupInviteData;
|
||||
import org.qora.data.group.GroupJoinRequestData;
|
||||
import org.qora.data.group.GroupMemberData;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.GroupRepository;
|
||||
@ -181,7 +183,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupAdminData> getAllGroupAdmins(String groupName) throws DataException {
|
||||
public List<GroupAdminData> getGroupAdmins(String groupName) throws DataException {
|
||||
List<GroupAdminData> admins = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT admin, group_reference FROM AccountGroupAdmins WHERE group_name = ?", groupName)) {
|
||||
@ -253,7 +255,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupMemberData> getAllGroupMembers(String groupName) throws DataException {
|
||||
public List<GroupMemberData> getGroupMembers(String groupName) throws DataException {
|
||||
List<GroupMemberData> members = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT address, joined, group_reference FROM AccountGroupMembers WHERE group_name = ?",
|
||||
@ -311,4 +313,178 @@ public class HSQLDBGroupRepository implements GroupRepository {
|
||||
}
|
||||
}
|
||||
|
||||
// Group Invites
|
||||
|
||||
@Override
|
||||
public GroupInviteData getInvite(String groupName, String inviter, String invitee) throws DataException {
|
||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT expiry, reference FROM AccountGroupInvites WHERE group_name = ?",
|
||||
groupName)) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
Timestamp expiryTimestamp = resultSet.getTimestamp(1, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Long expiry = expiryTimestamp == null ? null : expiryTimestamp.getTime();
|
||||
|
||||
byte[] reference = resultSet.getBytes(2);
|
||||
|
||||
return new GroupInviteData(groupName, inviter, invitee, expiry, reference);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch group invite from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasInvite(String groupName, String invitee) throws DataException {
|
||||
try {
|
||||
return this.repository.exists("AccountGroupInvites", "group_name = ? AND invitee = ?", groupName, invitee);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to check for group invite in repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean inviteExists(String groupName, String inviter, String invitee) throws DataException {
|
||||
try {
|
||||
return this.repository.exists("AccountGroupInvites", "group_name = ? AND inviter = ? AND invitee = ?", groupName, inviter, invitee);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to check for group invite in repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupInviteData> getGroupInvites(String groupName) throws DataException {
|
||||
List<GroupInviteData> invites = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT inviter, invitee, expiry, reference FROM AccountGroupInvites WHERE group_name = ?",
|
||||
groupName)) {
|
||||
if (resultSet == null)
|
||||
return invites;
|
||||
|
||||
do {
|
||||
String inviter = resultSet.getString(1);
|
||||
String invitee = resultSet.getString(2);
|
||||
|
||||
Timestamp expiryTimestamp = resultSet.getTimestamp(3, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Long expiry = expiryTimestamp == null ? null : expiryTimestamp.getTime();
|
||||
|
||||
byte[] reference = resultSet.getBytes(4);
|
||||
|
||||
invites.add(new GroupInviteData(groupName, inviter, invitee, expiry, reference));
|
||||
} while (resultSet.next());
|
||||
|
||||
return invites;
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch group invites from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupInviteData> getInvitesByInvitee(String groupName, String invitee) throws DataException {
|
||||
List<GroupInviteData> invites = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT inviter, expiry, reference FROM AccountGroupInvites WHERE group_name = ? AND invitee = ?", groupName, invitee)) {
|
||||
if (resultSet == null)
|
||||
return invites;
|
||||
|
||||
do {
|
||||
String inviter = resultSet.getString(1);
|
||||
|
||||
Timestamp expiryTimestamp = resultSet.getTimestamp(2, Calendar.getInstance(HSQLDBRepository.UTC));
|
||||
Long expiry = expiryTimestamp == null ? null : expiryTimestamp.getTime();
|
||||
|
||||
byte[] reference = resultSet.getBytes(3);
|
||||
|
||||
invites.add(new GroupInviteData(groupName, inviter, invitee, expiry, reference));
|
||||
} while (resultSet.next());
|
||||
|
||||
return invites;
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch group invites from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(GroupInviteData groupInviteData) throws DataException {
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountGroupInvites");
|
||||
|
||||
Timestamp expiryTimestamp;
|
||||
if (groupInviteData.getExpiry() == null)
|
||||
expiryTimestamp = null;
|
||||
else
|
||||
expiryTimestamp = new Timestamp(groupInviteData.getExpiry());
|
||||
|
||||
saveHelper.bind("group_name", groupInviteData.getGroupName()).bind("inviter", groupInviteData.getInviter())
|
||||
.bind("invitee", groupInviteData.getInvitee()).bind("expiry", expiryTimestamp).bind("reference", groupInviteData.getReference());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to save group invite into repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteInvite(String groupName, String inviter, String invitee) throws DataException {
|
||||
try {
|
||||
this.repository.delete("AccountGroupInvites", "group_name = ? AND inviter = ? AND invitee = ?", groupName, inviter, invitee);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete group invite from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Group Join Requests
|
||||
|
||||
@Override
|
||||
public boolean joinRequestExists(String groupName, String joiner) throws DataException {
|
||||
try {
|
||||
return this.repository.exists("AccountGroupJoinRequests", "group_name = ? AND joiner = ?", groupName, joiner);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to check for group join request in repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupJoinRequestData> getGroupJoinRequests(String groupName) throws DataException {
|
||||
List<GroupJoinRequestData> joinRequests = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT joiner FROM AccountGroupJoinRequests WHERE group_name = ?", groupName)) {
|
||||
if (resultSet == null)
|
||||
return joinRequests;
|
||||
|
||||
do {
|
||||
String joiner = resultSet.getString(1);
|
||||
|
||||
joinRequests.add(new GroupJoinRequestData(groupName, joiner));
|
||||
} while (resultSet.next());
|
||||
|
||||
return joinRequests;
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch group join requests from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(GroupJoinRequestData groupJoinRequestData) throws DataException {
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountGroupJoinRequests");
|
||||
|
||||
saveHelper.bind("group_name", groupJoinRequestData.getGroupName()).bind("joiner", groupJoinRequestData.getJoiner());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to save group join request into repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteJoinRequest(String groupName, String joiner) throws DataException {
|
||||
try {
|
||||
this.repository.delete("AccountGroupJoinRequests", "group_name = ? AND joiner = ?", groupName, joiner);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete group join request from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package org.qora.repository.hsqldb.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.qora.data.transaction.CancelGroupInviteTransactionData;
|
||||
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 HSQLDBCancelGroupInviteTransactionRepository extends HSQLDBTransactionRepository {
|
||||
|
||||
public HSQLDBCancelGroupInviteTransactionRepository(HSQLDBRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT group_name, invitee FROM CancelGroupInviteTransactions WHERE signature = ?", signature)) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
String groupName = resultSet.getString(1);
|
||||
String invitee = resultSet.getString(2);
|
||||
|
||||
return new CancelGroupInviteTransactionData(creatorPublicKey, groupName, invitee, fee, timestamp, reference, signature);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch cancel group invite transaction from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
CancelGroupInviteTransactionData cancelGroupInviteTransactionData = (CancelGroupInviteTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("CancelGroupInviteTransactions");
|
||||
|
||||
saveHelper.bind("signature", cancelGroupInviteTransactionData.getSignature()).bind("admin", cancelGroupInviteTransactionData.getAdminPublicKey())
|
||||
.bind("group_name", cancelGroupInviteTransactionData.getGroupName()).bind("invitee", cancelGroupInviteTransactionData.getInvitee());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to save cancel group invite transaction into repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.qora.repository.hsqldb.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||
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 HSQLDBGroupInviteTransactionRepository extends HSQLDBTransactionRepository {
|
||||
|
||||
public HSQLDBGroupInviteTransactionRepository(HSQLDBRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT group_name, invitee, time_to_live, group_reference FROM GroupInviteTransactions WHERE signature = ?", signature)) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
String groupName = resultSet.getString(1);
|
||||
String invitee = resultSet.getString(2);
|
||||
int timeToLive = resultSet.getInt(3);
|
||||
byte[] groupReference = resultSet.getBytes(4);
|
||||
|
||||
return new GroupInviteTransactionData(creatorPublicKey, groupName, invitee, timeToLive, groupReference, fee, timestamp, reference, signature);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch group invite transaction from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
GroupInviteTransactionData groupInviteTransactionData = (GroupInviteTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("GroupInviteTransactions");
|
||||
|
||||
saveHelper.bind("signature", groupInviteTransactionData.getSignature()).bind("admin", groupInviteTransactionData.getAdminPublicKey())
|
||||
.bind("group_name", groupInviteTransactionData.getGroupName()).bind("invitee", groupInviteTransactionData.getInvitee())
|
||||
.bind("time_to_live", groupInviteTransactionData.getTimeToLive()).bind("group_reference", groupInviteTransactionData.getGroupReference());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to save group invite transaction into repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupInviteTransactionData> getInvitesWithGroupReference(byte[] groupReference) throws DataException {
|
||||
List<GroupInviteTransactionData> invites = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository
|
||||
.checkedExecute("SELECT signature FROM GroupInviteTransactions WHERE group_reference = ?", groupReference)) {
|
||||
if (resultSet == null)
|
||||
return invites;
|
||||
|
||||
do {
|
||||
byte[] signature = resultSet.getBytes(1);
|
||||
|
||||
invites.add((GroupInviteTransactionData) this.repository.getTransactionRepository().fromSignature(signature));
|
||||
} while (resultSet.next());
|
||||
|
||||
return invites;
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch group invite transaction from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package org.qora.repository.hsqldb.transaction;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.qora.data.transaction.GroupKickTransactionData;
|
||||
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 HSQLDBGroupKickTransactionRepository extends HSQLDBTransactionRepository {
|
||||
|
||||
public HSQLDBGroupKickTransactionRepository(HSQLDBRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
TransactionData fromBase(byte[] signature, byte[] reference, byte[] creatorPublicKey, long timestamp, BigDecimal fee) throws DataException {
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(
|
||||
"SELECT group_name, address, reason, member_reference, admin_reference FROM GroupKickTransactions WHERE signature = ?", signature)) {
|
||||
if (resultSet == null)
|
||||
return null;
|
||||
|
||||
String groupName = resultSet.getString(1);
|
||||
String member = resultSet.getString(2);
|
||||
String reason = resultSet.getString(3);
|
||||
byte[] memberReference = resultSet.getBytes(4);
|
||||
byte[] adminReference = resultSet.getBytes(5);
|
||||
|
||||
return new GroupKickTransactionData(creatorPublicKey, groupName, member, reason, memberReference, adminReference, fee, timestamp, reference,
|
||||
signature);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch group kick transaction from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(TransactionData transactionData) throws DataException {
|
||||
GroupKickTransactionData groupKickTransactionData = (GroupKickTransactionData) transactionData;
|
||||
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("GroupKickTransactions");
|
||||
|
||||
saveHelper.bind("signature", groupKickTransactionData.getSignature()).bind("admin", groupKickTransactionData.getAdminPublicKey())
|
||||
.bind("group_name", groupKickTransactionData.getGroupName()).bind("address", groupKickTransactionData.getMember())
|
||||
.bind("reason", groupKickTransactionData.getReason()).bind("member_reference", groupKickTransactionData.getMemberReference())
|
||||
.bind("admin_reference", groupKickTransactionData.getAdminReference());
|
||||
|
||||
try {
|
||||
saveHelper.execute(this.repository);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to save group kick transaction into repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -11,6 +11,7 @@ import java.util.List;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.qora.data.PaymentData;
|
||||
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.TransactionRepository;
|
||||
@ -45,6 +46,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
private HSQLDBUpdateGroupTransactionRepository updateGroupTransactionRepository;
|
||||
private HSQLDBAddGroupAdminTransactionRepository addGroupAdminTransactionRepository;
|
||||
private HSQLDBRemoveGroupAdminTransactionRepository removeGroupAdminTransactionRepository;
|
||||
private HSQLDBGroupKickTransactionRepository groupKickTransactionRepository;
|
||||
private HSQLDBGroupInviteTransactionRepository groupInviteTransactionRepository;
|
||||
private HSQLDBCancelGroupInviteTransactionRepository cancelGroupInviteTransactionRepository;
|
||||
private HSQLDBJoinGroupTransactionRepository joinGroupTransactionRepository;
|
||||
private HSQLDBLeaveGroupTransactionRepository leaveGroupTransactionRepository;
|
||||
|
||||
@ -72,6 +76,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
this.updateGroupTransactionRepository = new HSQLDBUpdateGroupTransactionRepository(repository);
|
||||
this.addGroupAdminTransactionRepository = new HSQLDBAddGroupAdminTransactionRepository(repository);
|
||||
this.removeGroupAdminTransactionRepository = new HSQLDBRemoveGroupAdminTransactionRepository(repository);
|
||||
this.groupKickTransactionRepository = new HSQLDBGroupKickTransactionRepository(repository);
|
||||
this.groupInviteTransactionRepository = new HSQLDBGroupInviteTransactionRepository(repository);
|
||||
this.cancelGroupInviteTransactionRepository = new HSQLDBCancelGroupInviteTransactionRepository(repository);
|
||||
this.joinGroupTransactionRepository = new HSQLDBJoinGroupTransactionRepository(repository);
|
||||
this.leaveGroupTransactionRepository = new HSQLDBLeaveGroupTransactionRepository(repository);
|
||||
}
|
||||
@ -212,6 +219,15 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
case REMOVE_GROUP_ADMIN:
|
||||
return this.removeGroupAdminTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||
|
||||
case GROUP_KICK:
|
||||
return this.groupKickTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||
|
||||
case GROUP_INVITE:
|
||||
return this.groupInviteTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
return this.cancelGroupInviteTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||
|
||||
case JOIN_GROUP:
|
||||
return this.joinGroupTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||
|
||||
@ -554,6 +570,18 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
this.removeGroupAdminTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case GROUP_KICK:
|
||||
this.groupKickTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case GROUP_INVITE:
|
||||
this.groupInviteTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
this.cancelGroupInviteTransactionRepository.save(transactionData);
|
||||
break;
|
||||
|
||||
case JOIN_GROUP:
|
||||
this.joinGroupTransactionRepository.save(transactionData);
|
||||
break;
|
||||
@ -583,4 +611,10 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupInviteTransactionData> getInvitesWithGroupReference(byte[] groupReference) throws DataException {
|
||||
// Let specialized subclass handle this
|
||||
return this.groupInviteTransactionRepository.getInvitesWithGroupReference(groupReference);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import java.util.List;
|
||||
import org.qora.account.Account;
|
||||
import org.qora.account.PublicKeyAccount;
|
||||
import org.qora.asset.Asset;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
@ -96,12 +97,16 @@ public class AddGroupAdminTransaction extends Transaction {
|
||||
if (!owner.getAddress().equals(groupData.getOwner()))
|
||||
return ValidationResult.INVALID_GROUP_OWNER;
|
||||
|
||||
// Check address is a member
|
||||
if (!this.repository.getGroupRepository().memberExists(addGroupAdminTransactionData.getGroupName(), owner.getAddress()))
|
||||
return ValidationResult.NOT_GROUP_MEMBER;
|
||||
// Check member address is valid
|
||||
if (!Crypto.isValidAddress(addGroupAdminTransactionData.getMember()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
Account member = getMember();
|
||||
|
||||
// Check address is a member
|
||||
if (!this.repository.getGroupRepository().memberExists(addGroupAdminTransactionData.getGroupName(), member.getAddress()))
|
||||
return ValidationResult.NOT_GROUP_MEMBER;
|
||||
|
||||
// Check member is not already an admin
|
||||
if (this.repository.getGroupRepository().adminExists(addGroupAdminTransactionData.getGroupName(), member.getAddress()))
|
||||
return ValidationResult.ALREADY_GROUP_ADMIN;
|
||||
|
@ -0,0 +1,157 @@
|
||||
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.PublicKeyAccount;
|
||||
import org.qora.asset.Asset;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.transaction.CancelGroupInviteTransactionData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.group.Group;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.GroupRepository;
|
||||
import org.qora.repository.Repository;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
|
||||
public class CancelGroupInviteTransaction extends Transaction {
|
||||
|
||||
// Properties
|
||||
private CancelGroupInviteTransactionData cancelCancelGroupInviteTransactionData;
|
||||
|
||||
// Constructors
|
||||
|
||||
public CancelGroupInviteTransaction(Repository repository, TransactionData transactionData) {
|
||||
super(repository, transactionData);
|
||||
|
||||
this.cancelCancelGroupInviteTransactionData = (CancelGroupInviteTransactionData) 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.getAdmin().getAddress()))
|
||||
return true;
|
||||
|
||||
if (address.equals(this.getInvitee().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.getAdmin().getAddress()))
|
||||
amount = amount.subtract(this.transactionData.getFee());
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
public Account getAdmin() throws DataException {
|
||||
return new PublicKeyAccount(this.repository, this.cancelCancelGroupInviteTransactionData.getAdminPublicKey());
|
||||
}
|
||||
|
||||
public Account getInvitee() throws DataException {
|
||||
return new Account(this.repository, this.cancelCancelGroupInviteTransactionData.getInvitee());
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = cancelCancelGroupInviteTransactionData.getGroupName();
|
||||
|
||||
// Check member address is valid
|
||||
if (!Crypto.isValidAddress(cancelCancelGroupInviteTransactionData.getInvitee()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
// Check group name size bounds
|
||||
int groupNameLength = Utf8.encodedLength(groupName);
|
||||
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)
|
||||
return ValidationResult.INVALID_NAME_LENGTH;
|
||||
|
||||
// Check group name is lowercase
|
||||
if (!groupName.equals(groupName.toLowerCase()))
|
||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||
|
||||
GroupData groupData = groupRepository.fromGroupName(groupName);
|
||||
|
||||
// Check group exists
|
||||
if (groupData == null)
|
||||
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
||||
|
||||
Account admin = getAdmin();
|
||||
Account invitee = getInvitee();
|
||||
|
||||
// Check invite exists
|
||||
if (!groupRepository.inviteExists(groupName, admin.getAddress(), invitee.getAddress()))
|
||||
return ValidationResult.INVITE_UNKNOWN;
|
||||
|
||||
// Check fee is positive
|
||||
if (cancelCancelGroupInviteTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
if (!Arrays.equals(admin.getLastReference(), cancelCancelGroupInviteTransactionData.getReference()))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check creator has enough funds
|
||||
if (admin.getConfirmedBalance(Asset.QORA).compareTo(cancelCancelGroupInviteTransactionData.getFee()) < 0)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() throws DataException {
|
||||
// Update Group Membership
|
||||
Group group = new Group(this.repository, cancelCancelGroupInviteTransactionData.getGroupName());
|
||||
group.cancelInvite(cancelCancelGroupInviteTransactionData);
|
||||
|
||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
||||
this.repository.getTransactionRepository().save(cancelCancelGroupInviteTransactionData);
|
||||
|
||||
// Update admin's balance
|
||||
Account admin = getAdmin();
|
||||
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(cancelCancelGroupInviteTransactionData.getFee()));
|
||||
|
||||
// Update admin's reference
|
||||
admin.setLastReference(cancelCancelGroupInviteTransactionData.getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan() throws DataException {
|
||||
// Revert group membership
|
||||
Group group = new Group(this.repository, cancelCancelGroupInviteTransactionData.getGroupName());
|
||||
group.uncancelInvite(cancelCancelGroupInviteTransactionData);
|
||||
|
||||
// Delete this transaction itself
|
||||
this.repository.getTransactionRepository().delete(cancelCancelGroupInviteTransactionData);
|
||||
|
||||
// Update admin's balance
|
||||
Account admin = getAdmin();
|
||||
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(cancelCancelGroupInviteTransactionData.getFee()));
|
||||
|
||||
// Update admin's reference
|
||||
admin.setLastReference(cancelCancelGroupInviteTransactionData.getReference());
|
||||
}
|
||||
|
||||
}
|
165
src/main/java/org/qora/transaction/GroupInviteTransaction.java
Normal file
165
src/main/java/org/qora/transaction/GroupInviteTransaction.java
Normal file
@ -0,0 +1,165 @@
|
||||
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.PublicKeyAccount;
|
||||
import org.qora.asset.Asset;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.group.Group;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.GroupRepository;
|
||||
import org.qora.repository.Repository;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
|
||||
public class GroupInviteTransaction extends Transaction {
|
||||
|
||||
// Properties
|
||||
private GroupInviteTransactionData groupInviteTransactionData;
|
||||
|
||||
// Constructors
|
||||
|
||||
public GroupInviteTransaction(Repository repository, TransactionData transactionData) {
|
||||
super(repository, transactionData);
|
||||
|
||||
this.groupInviteTransactionData = (GroupInviteTransactionData) 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.getAdmin().getAddress()))
|
||||
return true;
|
||||
|
||||
if (address.equals(this.getInvitee().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.getAdmin().getAddress()))
|
||||
amount = amount.subtract(this.transactionData.getFee());
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
public Account getAdmin() throws DataException {
|
||||
return new PublicKeyAccount(this.repository, this.groupInviteTransactionData.getAdminPublicKey());
|
||||
}
|
||||
|
||||
public Account getInvitee() throws DataException {
|
||||
return new Account(this.repository, this.groupInviteTransactionData.getInvitee());
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = groupInviteTransactionData.getGroupName();
|
||||
|
||||
// Check time to live zero (infinite) or positive
|
||||
if (groupInviteTransactionData.getTimeToLive() < 0)
|
||||
return ValidationResult.INVALID_LIFETIME;
|
||||
|
||||
// Check member address is valid
|
||||
if (!Crypto.isValidAddress(groupInviteTransactionData.getInvitee()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
// Check group name size bounds
|
||||
int groupNameLength = Utf8.encodedLength(groupName);
|
||||
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)
|
||||
return ValidationResult.INVALID_NAME_LENGTH;
|
||||
|
||||
// Check group name is lowercase
|
||||
if (!groupName.equals(groupName.toLowerCase()))
|
||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||
|
||||
GroupData groupData = groupRepository.fromGroupName(groupName);
|
||||
|
||||
// Check group exists
|
||||
if (groupData == null)
|
||||
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
||||
|
||||
Account admin = getAdmin();
|
||||
Account invitee = getInvitee();
|
||||
|
||||
// Can't invite if not an admin
|
||||
if (!groupRepository.adminExists(groupName, admin.getAddress()))
|
||||
return ValidationResult.NOT_GROUP_ADMIN;
|
||||
|
||||
// Check invitee not already in group
|
||||
if (groupRepository.memberExists(groupName, invitee.getAddress()))
|
||||
return ValidationResult.ALREADY_GROUP_MEMBER;
|
||||
|
||||
// Check fee is positive
|
||||
if (groupInviteTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
if (!Arrays.equals(admin.getLastReference(), groupInviteTransactionData.getReference()))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check creator has enough funds
|
||||
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupInviteTransactionData.getFee()) < 0)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() throws DataException {
|
||||
// Update Group Membership
|
||||
Group group = new Group(this.repository, groupInviteTransactionData.getGroupName());
|
||||
group.invite(groupInviteTransactionData);
|
||||
|
||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
||||
this.repository.getTransactionRepository().save(groupInviteTransactionData);
|
||||
|
||||
// Update admin's balance
|
||||
Account admin = getAdmin();
|
||||
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupInviteTransactionData.getFee()));
|
||||
|
||||
// Update admin's reference
|
||||
admin.setLastReference(groupInviteTransactionData.getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan() throws DataException {
|
||||
// Revert group membership
|
||||
Group group = new Group(this.repository, groupInviteTransactionData.getGroupName());
|
||||
group.uninvite(groupInviteTransactionData);
|
||||
|
||||
// Delete this transaction itself
|
||||
this.repository.getTransactionRepository().delete(groupInviteTransactionData);
|
||||
|
||||
// Update admin's balance
|
||||
Account admin = getAdmin();
|
||||
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupInviteTransactionData.getFee()));
|
||||
|
||||
// Update admin's reference
|
||||
admin.setLastReference(groupInviteTransactionData.getReference());
|
||||
}
|
||||
|
||||
}
|
165
src/main/java/org/qora/transaction/GroupKickTransaction.java
Normal file
165
src/main/java/org/qora/transaction/GroupKickTransaction.java
Normal file
@ -0,0 +1,165 @@
|
||||
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.PublicKeyAccount;
|
||||
import org.qora.asset.Asset;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.transaction.GroupKickTransactionData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.group.Group;
|
||||
import org.qora.repository.DataException;
|
||||
import org.qora.repository.GroupRepository;
|
||||
import org.qora.repository.Repository;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
|
||||
public class GroupKickTransaction extends Transaction {
|
||||
|
||||
// Properties
|
||||
private GroupKickTransactionData groupKickTransactionData;
|
||||
|
||||
// Constructors
|
||||
|
||||
public GroupKickTransaction(Repository repository, TransactionData transactionData) {
|
||||
super(repository, transactionData);
|
||||
|
||||
this.groupKickTransactionData = (GroupKickTransactionData) 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.getAdmin().getAddress()))
|
||||
return true;
|
||||
|
||||
if (address.equals(this.getMember().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.getAdmin().getAddress()))
|
||||
amount = amount.subtract(this.transactionData.getFee());
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
// Navigation
|
||||
|
||||
public Account getAdmin() throws DataException {
|
||||
return new PublicKeyAccount(this.repository, this.groupKickTransactionData.getAdminPublicKey());
|
||||
}
|
||||
|
||||
public Account getMember() throws DataException {
|
||||
return new Account(this.repository, this.groupKickTransactionData.getMember());
|
||||
}
|
||||
|
||||
// Processing
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||
String groupName = groupKickTransactionData.getGroupName();
|
||||
|
||||
// Check member address is valid
|
||||
if (!Crypto.isValidAddress(groupKickTransactionData.getMember()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
// Check group name size bounds
|
||||
int groupNameLength = Utf8.encodedLength(groupName);
|
||||
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)
|
||||
return ValidationResult.INVALID_NAME_LENGTH;
|
||||
|
||||
// Check group name is lowercase
|
||||
if (!groupName.equals(groupName.toLowerCase()))
|
||||
return ValidationResult.NAME_NOT_LOWER_CASE;
|
||||
|
||||
GroupData groupData = groupRepository.fromGroupName(groupName);
|
||||
|
||||
// Check group exists
|
||||
if (groupData == null)
|
||||
return ValidationResult.GROUP_DOES_NOT_EXIST;
|
||||
|
||||
Account admin = getAdmin();
|
||||
Account member = getMember();
|
||||
|
||||
// Can't kick if not an admin
|
||||
if (!groupRepository.adminExists(groupName, admin.getAddress()))
|
||||
return ValidationResult.NOT_GROUP_ADMIN;
|
||||
|
||||
// Check member actually in group
|
||||
if (!groupRepository.memberExists(groupName, member.getAddress()))
|
||||
return ValidationResult.NOT_GROUP_MEMBER;
|
||||
|
||||
// Can't kick another admin unless the group owner
|
||||
if (!admin.getAddress().equals(groupData.getOwner()) && groupRepository.adminExists(groupName, member.getAddress()))
|
||||
return ValidationResult.INVALID_GROUP_OWNER;
|
||||
|
||||
// Check fee is positive
|
||||
if (groupKickTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
||||
if (!Arrays.equals(admin.getLastReference(), groupKickTransactionData.getReference()))
|
||||
return ValidationResult.INVALID_REFERENCE;
|
||||
|
||||
// Check creator has enough funds
|
||||
if (admin.getConfirmedBalance(Asset.QORA).compareTo(groupKickTransactionData.getFee()) < 0)
|
||||
return ValidationResult.NO_BALANCE;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process() throws DataException {
|
||||
// Update Group Membership
|
||||
Group group = new Group(this.repository, groupKickTransactionData.getGroupName());
|
||||
group.kick(groupKickTransactionData);
|
||||
|
||||
// Save this transaction with updated member/admin references to transactions that can help restore state
|
||||
this.repository.getTransactionRepository().save(groupKickTransactionData);
|
||||
|
||||
// Update admin's balance
|
||||
Account admin = getAdmin();
|
||||
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).subtract(groupKickTransactionData.getFee()));
|
||||
|
||||
// Update admin's reference
|
||||
admin.setLastReference(groupKickTransactionData.getSignature());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void orphan() throws DataException {
|
||||
// Revert group membership
|
||||
Group group = new Group(this.repository, groupKickTransactionData.getGroupName());
|
||||
group.unkick(groupKickTransactionData);
|
||||
|
||||
// Delete this transaction itself
|
||||
this.repository.getTransactionRepository().delete(groupKickTransactionData);
|
||||
|
||||
// Update admin's balance
|
||||
Account admin = getAdmin();
|
||||
admin.setConfirmedBalance(Asset.QORA, admin.getConfirmedBalance(Asset.QORA).add(groupKickTransactionData.getFee()));
|
||||
|
||||
// Update admin's reference
|
||||
admin.setLastReference(groupKickTransactionData.getReference());
|
||||
}
|
||||
|
||||
}
|
@ -88,6 +88,8 @@ public class JoinGroupTransaction extends Transaction {
|
||||
if (this.repository.getGroupRepository().memberExists(joinGroupTransactionData.getGroupName(), joiner.getAddress()))
|
||||
return ValidationResult.ALREADY_GROUP_MEMBER;
|
||||
|
||||
// XXX Check member is not banned
|
||||
|
||||
// Check fee is positive
|
||||
if (joinGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||
return ValidationResult.NEGATIVE_FEE;
|
||||
|
@ -8,6 +8,7 @@ import java.util.List;
|
||||
import org.qora.account.Account;
|
||||
import org.qora.account.PublicKeyAccount;
|
||||
import org.qora.asset.Asset;
|
||||
import org.qora.crypto.Crypto;
|
||||
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
||||
import org.qora.data.group.GroupData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
@ -75,6 +76,10 @@ public class RemoveGroupAdminTransaction extends Transaction {
|
||||
|
||||
@Override
|
||||
public ValidationResult isValid() throws DataException {
|
||||
// Check admin address is valid
|
||||
if (!Crypto.isValidAddress(removeGroupAdminTransactionData.getAdmin()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
// Check group name size bounds
|
||||
int groupNameLength = Utf8.encodedLength(removeGroupAdminTransactionData.getGroupName());
|
||||
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)
|
||||
|
@ -133,6 +133,8 @@ public abstract class Transaction {
|
||||
NOT_GROUP_MEMBER(53),
|
||||
ALREADY_GROUP_ADMIN(54),
|
||||
NOT_GROUP_ADMIN(55),
|
||||
INVALID_LIFETIME(56),
|
||||
INVITE_UNKNOWN(57),
|
||||
NOT_YET_RELEASED(1000);
|
||||
|
||||
public final int value;
|
||||
@ -244,6 +246,15 @@ public abstract class Transaction {
|
||||
case REMOVE_GROUP_ADMIN:
|
||||
return new RemoveGroupAdminTransaction(repository, transactionData);
|
||||
|
||||
case GROUP_KICK:
|
||||
return new GroupKickTransaction(repository, transactionData);
|
||||
|
||||
case GROUP_INVITE:
|
||||
return new GroupInviteTransaction(repository, transactionData);
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
return new CancelGroupInviteTransaction(repository, transactionData);
|
||||
|
||||
case JOIN_GROUP:
|
||||
return new JoinGroupTransaction(repository, transactionData);
|
||||
|
||||
|
@ -0,0 +1,104 @@
|
||||
package org.qora.transform.transaction;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.qora.account.PublicKeyAccount;
|
||||
import org.qora.data.transaction.CancelGroupInviteTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.group.Group;
|
||||
import org.qora.transform.TransformationException;
|
||||
import org.qora.utils.Serialization;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
public class CancelGroupInviteTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
// Property lengths
|
||||
private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH;
|
||||
private static final int NAME_SIZE_LENGTH = INT_LENGTH;
|
||||
private static final int INVITEE_LENGTH = ADDRESS_LENGTH;
|
||||
|
||||
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + NAME_SIZE_LENGTH + INVITEE_LENGTH;
|
||||
|
||||
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||
long timestamp = byteBuffer.getLong();
|
||||
|
||||
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||
byteBuffer.get(reference);
|
||||
|
||||
byte[] adminPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||
|
||||
String groupName = Serialization.deserializeSizedString(byteBuffer, Group.MAX_NAME_SIZE);
|
||||
|
||||
String invitee = Serialization.deserializeAddress(byteBuffer);
|
||||
|
||||
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||
|
||||
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||
byteBuffer.get(signature);
|
||||
|
||||
return new CancelGroupInviteTransactionData(adminPublicKey, groupName, invitee, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||
CancelGroupInviteTransactionData cancelGroupInviteTransactionData = (CancelGroupInviteTransactionData) transactionData;
|
||||
|
||||
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(cancelGroupInviteTransactionData.getGroupName());
|
||||
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||
try {
|
||||
CancelGroupInviteTransactionData cancelGroupInviteTransactionData = (CancelGroupInviteTransactionData) transactionData;
|
||||
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
||||
bytes.write(Ints.toByteArray(cancelGroupInviteTransactionData.getType().value));
|
||||
bytes.write(Longs.toByteArray(cancelGroupInviteTransactionData.getTimestamp()));
|
||||
bytes.write(cancelGroupInviteTransactionData.getReference());
|
||||
|
||||
bytes.write(cancelGroupInviteTransactionData.getCreatorPublicKey());
|
||||
Serialization.serializeSizedString(bytes, cancelGroupInviteTransactionData.getGroupName());
|
||||
Serialization.serializeAddress(bytes, cancelGroupInviteTransactionData.getInvitee());
|
||||
|
||||
Serialization.serializeBigDecimal(bytes, cancelGroupInviteTransactionData.getFee());
|
||||
|
||||
if (cancelGroupInviteTransactionData.getSignature() != null)
|
||||
bytes.write(cancelGroupInviteTransactionData.getSignature());
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException | ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
||||
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
||||
|
||||
try {
|
||||
CancelGroupInviteTransactionData cancelGroupInviteTransactionData = (CancelGroupInviteTransactionData) transactionData;
|
||||
|
||||
byte[] adminPublicKey = cancelGroupInviteTransactionData.getAdminPublicKey();
|
||||
|
||||
json.put("admin", PublicKeyAccount.getAddress(adminPublicKey));
|
||||
json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString());
|
||||
|
||||
json.put("groupName", cancelGroupInviteTransactionData.getGroupName());
|
||||
json.put("invitee", cancelGroupInviteTransactionData.getInvitee());
|
||||
} catch (ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package org.qora.transform.transaction;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.qora.account.PublicKeyAccount;
|
||||
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.group.Group;
|
||||
import org.qora.transform.TransformationException;
|
||||
import org.qora.utils.Serialization;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
public class GroupInviteTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
// Property lengths
|
||||
private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH;
|
||||
private static final int NAME_SIZE_LENGTH = INT_LENGTH;
|
||||
private static final int INVITEE_LENGTH = ADDRESS_LENGTH;
|
||||
private static final int TTL_LENGTH = INT_LENGTH;
|
||||
|
||||
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + NAME_SIZE_LENGTH + INVITEE_LENGTH + TTL_LENGTH;
|
||||
|
||||
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||
long timestamp = byteBuffer.getLong();
|
||||
|
||||
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||
byteBuffer.get(reference);
|
||||
|
||||
byte[] adminPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||
|
||||
String groupName = Serialization.deserializeSizedString(byteBuffer, Group.MAX_NAME_SIZE);
|
||||
|
||||
String invitee = Serialization.deserializeAddress(byteBuffer);
|
||||
|
||||
int timeToLive = byteBuffer.getInt();
|
||||
|
||||
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||
|
||||
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||
byteBuffer.get(signature);
|
||||
|
||||
return new GroupInviteTransactionData(adminPublicKey, groupName, invitee, timeToLive, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||
GroupInviteTransactionData groupInviteTransactionData = (GroupInviteTransactionData) transactionData;
|
||||
|
||||
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(groupInviteTransactionData.getGroupName());
|
||||
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||
try {
|
||||
GroupInviteTransactionData groupInviteTransactionData = (GroupInviteTransactionData) transactionData;
|
||||
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
||||
bytes.write(Ints.toByteArray(groupInviteTransactionData.getType().value));
|
||||
bytes.write(Longs.toByteArray(groupInviteTransactionData.getTimestamp()));
|
||||
bytes.write(groupInviteTransactionData.getReference());
|
||||
|
||||
bytes.write(groupInviteTransactionData.getCreatorPublicKey());
|
||||
Serialization.serializeSizedString(bytes, groupInviteTransactionData.getGroupName());
|
||||
Serialization.serializeAddress(bytes, groupInviteTransactionData.getInvitee());
|
||||
bytes.write(Ints.toByteArray(groupInviteTransactionData.getTimeToLive()));
|
||||
|
||||
Serialization.serializeBigDecimal(bytes, groupInviteTransactionData.getFee());
|
||||
|
||||
if (groupInviteTransactionData.getSignature() != null)
|
||||
bytes.write(groupInviteTransactionData.getSignature());
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException | ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
||||
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
||||
|
||||
try {
|
||||
GroupInviteTransactionData groupInviteTransactionData = (GroupInviteTransactionData) transactionData;
|
||||
|
||||
byte[] adminPublicKey = groupInviteTransactionData.getAdminPublicKey();
|
||||
|
||||
json.put("admin", PublicKeyAccount.getAddress(adminPublicKey));
|
||||
json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString());
|
||||
|
||||
json.put("groupName", groupInviteTransactionData.getGroupName());
|
||||
json.put("invitee", groupInviteTransactionData.getInvitee());
|
||||
json.put("timeToLive", groupInviteTransactionData.getTimeToLive());
|
||||
} catch (ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
package org.qora.transform.transaction;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.qora.account.PublicKeyAccount;
|
||||
import org.qora.data.transaction.GroupKickTransactionData;
|
||||
import org.qora.data.transaction.TransactionData;
|
||||
import org.qora.group.Group;
|
||||
import org.qora.transform.TransformationException;
|
||||
import org.qora.utils.Serialization;
|
||||
|
||||
import com.google.common.base.Utf8;
|
||||
import com.google.common.hash.HashCode;
|
||||
import com.google.common.primitives.Ints;
|
||||
import com.google.common.primitives.Longs;
|
||||
|
||||
public class GroupKickTransactionTransformer extends TransactionTransformer {
|
||||
|
||||
// Property lengths
|
||||
private static final int ADMIN_LENGTH = PUBLIC_KEY_LENGTH;
|
||||
private static final int NAME_SIZE_LENGTH = INT_LENGTH;
|
||||
private static final int MEMBER_LENGTH = ADDRESS_LENGTH;
|
||||
private static final int REASON_SIZE_LENGTH = INT_LENGTH;
|
||||
|
||||
private static final int TYPELESS_DATALESS_LENGTH = BASE_TYPELESS_LENGTH + ADMIN_LENGTH + NAME_SIZE_LENGTH + MEMBER_LENGTH + REASON_SIZE_LENGTH;
|
||||
|
||||
static TransactionData fromByteBuffer(ByteBuffer byteBuffer) throws TransformationException {
|
||||
long timestamp = byteBuffer.getLong();
|
||||
|
||||
byte[] reference = new byte[REFERENCE_LENGTH];
|
||||
byteBuffer.get(reference);
|
||||
|
||||
byte[] adminPublicKey = Serialization.deserializePublicKey(byteBuffer);
|
||||
|
||||
String groupName = Serialization.deserializeSizedString(byteBuffer, Group.MAX_NAME_SIZE);
|
||||
|
||||
String member = Serialization.deserializeAddress(byteBuffer);
|
||||
|
||||
String reason = Serialization.deserializeSizedString(byteBuffer, Group.MAX_REASON_SIZE);
|
||||
|
||||
BigDecimal fee = Serialization.deserializeBigDecimal(byteBuffer);
|
||||
|
||||
byte[] signature = new byte[SIGNATURE_LENGTH];
|
||||
byteBuffer.get(signature);
|
||||
|
||||
return new GroupKickTransactionData(adminPublicKey, groupName, member, reason, fee, timestamp, reference, signature);
|
||||
}
|
||||
|
||||
public static int getDataLength(TransactionData transactionData) throws TransformationException {
|
||||
GroupKickTransactionData groupKickTransactionData = (GroupKickTransactionData) transactionData;
|
||||
|
||||
int dataLength = TYPE_LENGTH + TYPELESS_DATALESS_LENGTH + Utf8.encodedLength(groupKickTransactionData.getGroupName())
|
||||
+ Utf8.encodedLength(groupKickTransactionData.getReason());
|
||||
|
||||
return dataLength;
|
||||
}
|
||||
|
||||
public static byte[] toBytes(TransactionData transactionData) throws TransformationException {
|
||||
try {
|
||||
GroupKickTransactionData groupKickTransactionData = (GroupKickTransactionData) transactionData;
|
||||
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
||||
bytes.write(Ints.toByteArray(groupKickTransactionData.getType().value));
|
||||
bytes.write(Longs.toByteArray(groupKickTransactionData.getTimestamp()));
|
||||
bytes.write(groupKickTransactionData.getReference());
|
||||
|
||||
bytes.write(groupKickTransactionData.getCreatorPublicKey());
|
||||
Serialization.serializeSizedString(bytes, groupKickTransactionData.getGroupName());
|
||||
Serialization.serializeAddress(bytes, groupKickTransactionData.getMember());
|
||||
Serialization.serializeSizedString(bytes, groupKickTransactionData.getReason());
|
||||
|
||||
Serialization.serializeBigDecimal(bytes, groupKickTransactionData.getFee());
|
||||
|
||||
if (groupKickTransactionData.getSignature() != null)
|
||||
bytes.write(groupKickTransactionData.getSignature());
|
||||
|
||||
return bytes.toByteArray();
|
||||
} catch (IOException | ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static JSONObject toJSON(TransactionData transactionData) throws TransformationException {
|
||||
JSONObject json = TransactionTransformer.getBaseJSON(transactionData);
|
||||
|
||||
try {
|
||||
GroupKickTransactionData groupKickTransactionData = (GroupKickTransactionData) transactionData;
|
||||
|
||||
byte[] adminPublicKey = groupKickTransactionData.getAdminPublicKey();
|
||||
|
||||
json.put("admin", PublicKeyAccount.getAddress(adminPublicKey));
|
||||
json.put("adminPublicKey", HashCode.fromBytes(adminPublicKey).toString());
|
||||
|
||||
json.put("groupName", groupKickTransactionData.getGroupName());
|
||||
json.put("member", groupKickTransactionData.getMember());
|
||||
json.put("reason", groupKickTransactionData.getReason());
|
||||
} catch (ClassCastException e) {
|
||||
throw new TransformationException(e);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
}
|
@ -106,6 +106,15 @@ public class TransactionTransformer extends Transformer {
|
||||
case REMOVE_GROUP_ADMIN:
|
||||
return RemoveGroupAdminTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||
|
||||
case GROUP_KICK:
|
||||
return GroupKickTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||
|
||||
case GROUP_INVITE:
|
||||
return GroupInviteTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
return CancelGroupInviteTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||
|
||||
case JOIN_GROUP:
|
||||
return JoinGroupTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||
|
||||
@ -185,6 +194,15 @@ public class TransactionTransformer extends Transformer {
|
||||
case REMOVE_GROUP_ADMIN:
|
||||
return RemoveGroupAdminTransactionTransformer.getDataLength(transactionData);
|
||||
|
||||
case GROUP_KICK:
|
||||
return GroupKickTransactionTransformer.getDataLength(transactionData);
|
||||
|
||||
case GROUP_INVITE:
|
||||
return GroupInviteTransactionTransformer.getDataLength(transactionData);
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
return CancelGroupInviteTransactionTransformer.getDataLength(transactionData);
|
||||
|
||||
case JOIN_GROUP:
|
||||
return JoinGroupTransactionTransformer.getDataLength(transactionData);
|
||||
|
||||
@ -261,6 +279,15 @@ public class TransactionTransformer extends Transformer {
|
||||
case REMOVE_GROUP_ADMIN:
|
||||
return RemoveGroupAdminTransactionTransformer.toBytes(transactionData);
|
||||
|
||||
case GROUP_KICK:
|
||||
return GroupKickTransactionTransformer.toBytes(transactionData);
|
||||
|
||||
case GROUP_INVITE:
|
||||
return GroupInviteTransactionTransformer.toBytes(transactionData);
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
return CancelGroupInviteTransactionTransformer.toBytes(transactionData);
|
||||
|
||||
case JOIN_GROUP:
|
||||
return JoinGroupTransactionTransformer.toBytes(transactionData);
|
||||
|
||||
@ -346,6 +373,15 @@ public class TransactionTransformer extends Transformer {
|
||||
case REMOVE_GROUP_ADMIN:
|
||||
return RemoveGroupAdminTransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
case GROUP_KICK:
|
||||
return GroupKickTransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
case GROUP_INVITE:
|
||||
return GroupInviteTransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
return CancelGroupInviteTransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
case JOIN_GROUP:
|
||||
return JoinGroupTransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||
|
||||
@ -443,6 +479,15 @@ public class TransactionTransformer extends Transformer {
|
||||
case REMOVE_GROUP_ADMIN:
|
||||
return RemoveGroupAdminTransactionTransformer.toJSON(transactionData);
|
||||
|
||||
case GROUP_KICK:
|
||||
return GroupKickTransactionTransformer.toJSON(transactionData);
|
||||
|
||||
case GROUP_INVITE:
|
||||
return GroupInviteTransactionTransformer.toJSON(transactionData);
|
||||
|
||||
case CANCEL_GROUP_INVITE:
|
||||
return CancelGroupInviteTransactionTransformer.toJSON(transactionData);
|
||||
|
||||
case JOIN_GROUP:
|
||||
return JoinGroupTransactionTransformer.toJSON(transactionData);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user