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.crypto.Crypto;
|
||||||
import org.qora.data.group.GroupAdminData;
|
import org.qora.data.group.GroupAdminData;
|
||||||
import org.qora.data.group.GroupData;
|
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.group.GroupMemberData;
|
||||||
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
||||||
|
import org.qora.data.transaction.CancelGroupInviteTransactionData;
|
||||||
import org.qora.data.transaction.CreateGroupTransactionData;
|
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.JoinGroupTransactionData;
|
||||||
import org.qora.data.transaction.LeaveGroupTransactionData;
|
import org.qora.data.transaction.LeaveGroupTransactionData;
|
||||||
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
||||||
@ -43,7 +48,10 @@ import org.qora.transaction.Transaction;
|
|||||||
import org.qora.transaction.Transaction.ValidationResult;
|
import org.qora.transaction.Transaction.ValidationResult;
|
||||||
import org.qora.transform.TransformationException;
|
import org.qora.transform.TransformationException;
|
||||||
import org.qora.transform.transaction.AddGroupAdminTransactionTransformer;
|
import org.qora.transform.transaction.AddGroupAdminTransactionTransformer;
|
||||||
|
import org.qora.transform.transaction.CancelGroupInviteTransactionTransformer;
|
||||||
import org.qora.transform.transaction.CreateGroupTransactionTransformer;
|
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.JoinGroupTransactionTransformer;
|
||||||
import org.qora.transform.transaction.LeaveGroupTransactionTransformer;
|
import org.qora.transform.transaction.LeaveGroupTransactionTransformer;
|
||||||
import org.qora.transform.transaction.RemoveGroupAdminTransactionTransformer;
|
import org.qora.transform.transaction.RemoveGroupAdminTransactionTransformer;
|
||||||
@ -140,7 +148,7 @@ public class GroupsResource {
|
|||||||
Integer memberCount = null;
|
Integer memberCount = null;
|
||||||
|
|
||||||
if (includeMembers) {
|
if (includeMembers) {
|
||||||
groupMembers = repository.getGroupRepository().getAllGroupMembers(groupData.getGroupName());
|
groupMembers = repository.getGroupRepository().getGroupMembers(groupData.getGroupName());
|
||||||
|
|
||||||
// Strip groupName from member info
|
// Strip groupName from member info
|
||||||
groupMembers = groupMembers.stream().map(groupMemberData -> new GroupMemberData(null, groupMemberData.getMember(), groupMemberData.getJoined(), null)).collect(Collectors.toList());
|
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
|
// Always include admins
|
||||||
List<GroupAdminData> groupAdmins = repository.getGroupRepository().getAllGroupAdmins(groupData.getGroupName());
|
List<GroupAdminData> groupAdmins = repository.getGroupRepository().getGroupAdmins(groupData.getGroupName());
|
||||||
|
|
||||||
// We only need admin addresses
|
// We only need admin addresses
|
||||||
List<String> groupAdminAddresses = groupAdmins.stream().map(groupAdminData -> groupAdminData.getAdmin()).collect(Collectors.toList());
|
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
|
@POST
|
||||||
@Path("/join")
|
@Path("/join")
|
||||||
@Operation(
|
@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,
|
MultiPaymentTransactionData.class, DeployATTransactionData.class, MessageTransactionData.class, ATTransactionData.class,
|
||||||
CreateGroupTransactionData.class, UpdateGroupTransactionData.class,
|
CreateGroupTransactionData.class, UpdateGroupTransactionData.class,
|
||||||
AddGroupAdminTransactionData.class, RemoveGroupAdminTransactionData.class,
|
AddGroupAdminTransactionData.class, RemoveGroupAdminTransactionData.class,
|
||||||
|
GroupKickTransactionData.class, GroupInviteTransactionData.class,
|
||||||
JoinGroupTransactionData.class, LeaveGroupTransactionData.class
|
JoinGroupTransactionData.class, LeaveGroupTransactionData.class
|
||||||
})
|
})
|
||||||
//All properties to be converted to JSON via JAX-RS
|
//All properties to be converted to JSON via JAX-RS
|
||||||
|
@ -1,14 +1,20 @@
|
|||||||
package org.qora.group;
|
package org.qora.group;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.qora.account.Account;
|
import org.qora.account.Account;
|
||||||
import org.qora.account.PublicKeyAccount;
|
import org.qora.account.PublicKeyAccount;
|
||||||
import org.qora.data.group.GroupAdminData;
|
import org.qora.data.group.GroupAdminData;
|
||||||
import org.qora.data.group.GroupData;
|
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.group.GroupMemberData;
|
||||||
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
||||||
|
import org.qora.data.transaction.CancelGroupInviteTransactionData;
|
||||||
import org.qora.data.transaction.CreateGroupTransactionData;
|
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.JoinGroupTransactionData;
|
||||||
import org.qora.data.transaction.LeaveGroupTransactionData;
|
import org.qora.data.transaction.LeaveGroupTransactionData;
|
||||||
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
||||||
@ -27,6 +33,8 @@ public class Group {
|
|||||||
// Useful constants
|
// Useful constants
|
||||||
public static final int MAX_NAME_SIZE = 400;
|
public static final int MAX_NAME_SIZE = 400;
|
||||||
public static final int MAX_DESCRIPTION_SIZE = 4000;
|
public static final int MAX_DESCRIPTION_SIZE = 4000;
|
||||||
|
/** Max size of kick/ban reason */
|
||||||
|
public static final int MAX_REASON_SIZE = 400;
|
||||||
|
|
||||||
// Constructors
|
// Constructors
|
||||||
|
|
||||||
@ -38,9 +46,9 @@ public class Group {
|
|||||||
*/
|
*/
|
||||||
public Group(Repository repository, CreateGroupTransactionData createGroupTransactionData) {
|
public Group(Repository repository, CreateGroupTransactionData createGroupTransactionData) {
|
||||||
this.repository = repository;
|
this.repository = repository;
|
||||||
this.groupData = new GroupData(createGroupTransactionData.getOwner(),
|
this.groupData = new GroupData(createGroupTransactionData.getOwner(), createGroupTransactionData.getGroupName(),
|
||||||
createGroupTransactionData.getGroupName(), createGroupTransactionData.getDescription(), createGroupTransactionData.getTimestamp(),
|
createGroupTransactionData.getDescription(), createGroupTransactionData.getTimestamp(), createGroupTransactionData.getIsOpen(),
|
||||||
createGroupTransactionData.getIsOpen(), createGroupTransactionData.getSignature());
|
createGroupTransactionData.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,10 +69,12 @@ public class Group {
|
|||||||
this.repository.getGroupRepository().save(this.groupData);
|
this.repository.getGroupRepository().save(this.groupData);
|
||||||
|
|
||||||
// Add owner as admin too
|
// 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
|
// 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 {
|
public void uncreate() throws DataException {
|
||||||
@ -124,7 +134,8 @@ public class Group {
|
|||||||
|
|
||||||
// New owner should be a member if not already
|
// New owner should be a member if not already
|
||||||
if (!groupRepository.memberExists(groupName, newOwner)) {
|
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);
|
groupRepository.save(groupMemberData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +179,8 @@ public class Group {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void promoteToAdmin(AddGroupAdminTransactionData addGroupAdminTransactionData) throws DataException {
|
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);
|
this.repository.getGroupRepository().save(groupAdminData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,17 +211,160 @@ public class Group {
|
|||||||
groupRepository.save(groupAdminData);
|
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 {
|
public void join(JoinGroupTransactionData joinGroupTransactionData) throws DataException {
|
||||||
|
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||||
|
String groupName = joinGroupTransactionData.getGroupName();
|
||||||
Account joiner = new PublicKeyAccount(this.repository, joinGroupTransactionData.getJoinerPublicKey());
|
Account joiner = new PublicKeyAccount(this.repository, joinGroupTransactionData.getJoinerPublicKey());
|
||||||
|
|
||||||
GroupMemberData groupMemberData = new GroupMemberData(joinGroupTransactionData.getGroupName(), joiner.getAddress(), joinGroupTransactionData.getTimestamp(), joinGroupTransactionData.getSignature());
|
// Set invite transactions' group-reference to this transaction's signature so the invites can be put back if we orphan this join
|
||||||
this.repository.getGroupRepository().save(groupMemberData);
|
// 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 {
|
public void unjoin(JoinGroupTransactionData joinGroupTransactionData) throws DataException {
|
||||||
|
GroupRepository groupRepository = this.repository.getGroupRepository();
|
||||||
|
String groupName = joinGroupTransactionData.getGroupName();
|
||||||
Account joiner = new PublicKeyAccount(this.repository, joinGroupTransactionData.getJoinerPublicKey());
|
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 {
|
public void leave(LeaveGroupTransactionData leaveGroupTransactionData) throws DataException {
|
||||||
@ -249,7 +404,8 @@ public class Group {
|
|||||||
|
|
||||||
// Rejoin as member
|
// Rejoin as member
|
||||||
TransactionData membershipTransactionData = this.repository.getTransactionRepository().fromSignature(leaveGroupTransactionData.getMemberReference());
|
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
|
// Put back any admin state based on referenced group-related transaction
|
||||||
byte[] adminTransactionSignature = leaveGroupTransactionData.getAdminReference();
|
byte[] adminTransactionSignature = leaveGroupTransactionData.getAdminReference();
|
||||||
|
@ -4,6 +4,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.qora.data.group.GroupAdminData;
|
import org.qora.data.group.GroupAdminData;
|
||||||
import org.qora.data.group.GroupData;
|
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.group.GroupMemberData;
|
||||||
|
|
||||||
public interface GroupRepository {
|
public interface GroupRepository {
|
||||||
@ -28,7 +30,7 @@ public interface GroupRepository {
|
|||||||
|
|
||||||
public boolean adminExists(String groupName, String address) throws DataException;
|
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;
|
public void save(GroupAdminData groupAdminData) throws DataException;
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ public interface GroupRepository {
|
|||||||
|
|
||||||
public boolean memberExists(String groupName, String address) throws DataException;
|
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 */
|
/** Returns number of group members, or null if group doesn't exist */
|
||||||
public Integer countGroupMembers(String groupName) throws DataException;
|
public Integer countGroupMembers(String groupName) throws DataException;
|
||||||
@ -49,4 +51,30 @@ public interface GroupRepository {
|
|||||||
|
|
||||||
public void deleteMember(String groupName, String address) throws DataException;
|
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 java.util.List;
|
||||||
|
|
||||||
|
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||||
import org.qora.data.transaction.TransactionData;
|
import org.qora.data.transaction.TransactionData;
|
||||||
import org.qora.transaction.Transaction.TransactionType;
|
import org.qora.transaction.Transaction.TransactionType;
|
||||||
|
|
||||||
@ -52,4 +53,8 @@ public interface TransactionRepository {
|
|||||||
|
|
||||||
public void delete(TransactionData transactionData) throws DataException;
|
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;
|
break;
|
||||||
|
|
||||||
case 28:
|
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
|
// Account groups
|
||||||
stmt.execute("CREATE TABLE AccountGroups (group_name GroupName, owner QoraAddress NOT NULL, description GenericDescription NOT NULL, "
|
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, "
|
+ "created TIMESTAMP WITH TIME ZONE NOT NULL, updated TIMESTAMP WITH TIME ZONE, is_open BOOLEAN NOT NULL, "
|
||||||
+ "reference Signature, PRIMARY KEY (group_name))");
|
+ "reference Signature, PRIMARY KEY (group_name))");
|
||||||
// For finding groups by owner
|
// For finding groups by owner
|
||||||
stmt.execute("CREATE INDEX AccountGroupOwnerIndex on AccountGroups (owner)");
|
stmt.execute("CREATE INDEX AccountGroupOwnerIndex ON AccountGroups (owner)");
|
||||||
|
|
||||||
// Admins
|
// Admins
|
||||||
stmt.execute("CREATE TABLE AccountGroupAdmins (group_name GroupName, admin QoraAddress, group_reference Signature NOT NULL, PRIMARY KEY (group_name, admin))");
|
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
|
// For finding groups that address administrates
|
||||||
stmt.execute("CREATE INDEX AccountGroupAdminIndex on AccountGroupAdmins (admin)");
|
stmt.execute("CREATE INDEX AccountGroupAdminIndex ON AccountGroupAdmins (admin)");
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
stmt.execute("CREATE TABLE AccountGroupMembers (group_name GroupName, address QoraAddress, joined TIMESTAMP WITH TIME ZONE NOT NULL, group_reference Signature NOT NULL, "
|
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))");
|
+ "PRIMARY KEY (group_name, address))");
|
||||||
// For finding groups that address is member
|
// For finding groups that address is member
|
||||||
stmt.execute("CREATE INDEX AccountGroupMemberIndex on AccountGroupMembers (address)");
|
stmt.execute("CREATE INDEX AccountGroupMemberIndex ON AccountGroupMembers (address)");
|
||||||
|
|
||||||
// Invites
|
// Invites
|
||||||
// PRIMARY KEY (invitee + group + inviter) because most queries will be "what have I been invited to?" from UI
|
// 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, "
|
stmt.execute("CREATE TABLE AccountGroupInvites (group_name GroupName, inviter QoraAddress, invitee QoraAddress, "
|
||||||
+ "expiry TIMESTAMP WITH TIME ZONE NOT NULL, PRIMARY KEY (invitee, group_name, inviter))");
|
+ "expiry TIMESTAMP WITH TIME ZONE NOT NULL, reference Signature, PRIMARY KEY (invitee, group_name, inviter))");
|
||||||
// For finding invites sent by 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
|
// 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
|
// Bans
|
||||||
// NULL expiry means does not expire!
|
// 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, "
|
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))");
|
+ "reason GenericDescription NOT NULL, expiry TIMESTAMP WITH TIME ZONE, PRIMARY KEY (group_name, offender))");
|
||||||
// For expiry maintenance
|
// For expiry maintenance
|
||||||
stmt.execute("CREATE INDEX AccountGroupBanExpiryIndex on AccountGroupBans (expiry)");
|
stmt.execute("CREATE INDEX AccountGroupBanExpiryIndex ON AccountGroupBans (expiry)");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 31:
|
case 29:
|
||||||
// Account group transactions
|
// Account group transactions
|
||||||
stmt.execute("CREATE TABLE CreateGroupTransactions (signature Signature, creator QoraPublicKey NOT NULL, group_name GroupName NOT NULL, "
|
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, "
|
+ "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, "
|
stmt.execute("CREATE TABLE LeaveGroupTransactions (signature Signature, leaver QoraPublicKey NOT NULL, group_name GroupName NOT NULL, "
|
||||||
+ "member_reference Signature, admin_reference Signature, "
|
+ "member_reference Signature, admin_reference Signature, "
|
||||||
+ "PRIMARY KEY (signature), FOREIGN KEY (signature) REFERENCES Transactions (signature) ON DELETE CASCADE)");
|
+ "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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -9,6 +9,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import org.qora.data.group.GroupAdminData;
|
import org.qora.data.group.GroupAdminData;
|
||||||
import org.qora.data.group.GroupData;
|
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.group.GroupMemberData;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
import org.qora.repository.GroupRepository;
|
import org.qora.repository.GroupRepository;
|
||||||
@ -181,7 +183,7 @@ public class HSQLDBGroupRepository implements GroupRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<GroupAdminData> getAllGroupAdmins(String groupName) throws DataException {
|
public List<GroupAdminData> getGroupAdmins(String groupName) throws DataException {
|
||||||
List<GroupAdminData> admins = new ArrayList<>();
|
List<GroupAdminData> admins = new ArrayList<>();
|
||||||
|
|
||||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT admin, group_reference FROM AccountGroupAdmins WHERE group_name = ?", groupName)) {
|
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
|
@Override
|
||||||
public List<GroupMemberData> getAllGroupMembers(String groupName) throws DataException {
|
public List<GroupMemberData> getGroupMembers(String groupName) throws DataException {
|
||||||
List<GroupMemberData> members = new ArrayList<>();
|
List<GroupMemberData> members = new ArrayList<>();
|
||||||
|
|
||||||
try (ResultSet resultSet = this.repository.checkedExecute("SELECT address, joined, group_reference FROM AccountGroupMembers WHERE group_name = ?",
|
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.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.qora.data.PaymentData;
|
import org.qora.data.PaymentData;
|
||||||
|
import org.qora.data.transaction.GroupInviteTransactionData;
|
||||||
import org.qora.data.transaction.TransactionData;
|
import org.qora.data.transaction.TransactionData;
|
||||||
import org.qora.repository.DataException;
|
import org.qora.repository.DataException;
|
||||||
import org.qora.repository.TransactionRepository;
|
import org.qora.repository.TransactionRepository;
|
||||||
@ -45,6 +46,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
private HSQLDBUpdateGroupTransactionRepository updateGroupTransactionRepository;
|
private HSQLDBUpdateGroupTransactionRepository updateGroupTransactionRepository;
|
||||||
private HSQLDBAddGroupAdminTransactionRepository addGroupAdminTransactionRepository;
|
private HSQLDBAddGroupAdminTransactionRepository addGroupAdminTransactionRepository;
|
||||||
private HSQLDBRemoveGroupAdminTransactionRepository removeGroupAdminTransactionRepository;
|
private HSQLDBRemoveGroupAdminTransactionRepository removeGroupAdminTransactionRepository;
|
||||||
|
private HSQLDBGroupKickTransactionRepository groupKickTransactionRepository;
|
||||||
|
private HSQLDBGroupInviteTransactionRepository groupInviteTransactionRepository;
|
||||||
|
private HSQLDBCancelGroupInviteTransactionRepository cancelGroupInviteTransactionRepository;
|
||||||
private HSQLDBJoinGroupTransactionRepository joinGroupTransactionRepository;
|
private HSQLDBJoinGroupTransactionRepository joinGroupTransactionRepository;
|
||||||
private HSQLDBLeaveGroupTransactionRepository leaveGroupTransactionRepository;
|
private HSQLDBLeaveGroupTransactionRepository leaveGroupTransactionRepository;
|
||||||
|
|
||||||
@ -72,6 +76,9 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
this.updateGroupTransactionRepository = new HSQLDBUpdateGroupTransactionRepository(repository);
|
this.updateGroupTransactionRepository = new HSQLDBUpdateGroupTransactionRepository(repository);
|
||||||
this.addGroupAdminTransactionRepository = new HSQLDBAddGroupAdminTransactionRepository(repository);
|
this.addGroupAdminTransactionRepository = new HSQLDBAddGroupAdminTransactionRepository(repository);
|
||||||
this.removeGroupAdminTransactionRepository = new HSQLDBRemoveGroupAdminTransactionRepository(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.joinGroupTransactionRepository = new HSQLDBJoinGroupTransactionRepository(repository);
|
||||||
this.leaveGroupTransactionRepository = new HSQLDBLeaveGroupTransactionRepository(repository);
|
this.leaveGroupTransactionRepository = new HSQLDBLeaveGroupTransactionRepository(repository);
|
||||||
}
|
}
|
||||||
@ -212,6 +219,15 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
case REMOVE_GROUP_ADMIN:
|
case REMOVE_GROUP_ADMIN:
|
||||||
return this.removeGroupAdminTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
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:
|
case JOIN_GROUP:
|
||||||
return this.joinGroupTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
return this.joinGroupTransactionRepository.fromBase(signature, reference, creatorPublicKey, timestamp, fee);
|
||||||
|
|
||||||
@ -554,6 +570,18 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
|||||||
this.removeGroupAdminTransactionRepository.save(transactionData);
|
this.removeGroupAdminTransactionRepository.save(transactionData);
|
||||||
break;
|
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:
|
case JOIN_GROUP:
|
||||||
this.joinGroupTransactionRepository.save(transactionData);
|
this.joinGroupTransactionRepository.save(transactionData);
|
||||||
break;
|
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.Account;
|
||||||
import org.qora.account.PublicKeyAccount;
|
import org.qora.account.PublicKeyAccount;
|
||||||
import org.qora.asset.Asset;
|
import org.qora.asset.Asset;
|
||||||
|
import org.qora.crypto.Crypto;
|
||||||
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
import org.qora.data.transaction.AddGroupAdminTransactionData;
|
||||||
import org.qora.data.group.GroupData;
|
import org.qora.data.group.GroupData;
|
||||||
import org.qora.data.transaction.TransactionData;
|
import org.qora.data.transaction.TransactionData;
|
||||||
@ -96,12 +97,16 @@ public class AddGroupAdminTransaction extends Transaction {
|
|||||||
if (!owner.getAddress().equals(groupData.getOwner()))
|
if (!owner.getAddress().equals(groupData.getOwner()))
|
||||||
return ValidationResult.INVALID_GROUP_OWNER;
|
return ValidationResult.INVALID_GROUP_OWNER;
|
||||||
|
|
||||||
// Check address is a member
|
// Check member address is valid
|
||||||
if (!this.repository.getGroupRepository().memberExists(addGroupAdminTransactionData.getGroupName(), owner.getAddress()))
|
if (!Crypto.isValidAddress(addGroupAdminTransactionData.getMember()))
|
||||||
return ValidationResult.NOT_GROUP_MEMBER;
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
Account member = getMember();
|
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
|
// Check member is not already an admin
|
||||||
if (this.repository.getGroupRepository().adminExists(addGroupAdminTransactionData.getGroupName(), member.getAddress()))
|
if (this.repository.getGroupRepository().adminExists(addGroupAdminTransactionData.getGroupName(), member.getAddress()))
|
||||||
return ValidationResult.ALREADY_GROUP_ADMIN;
|
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()))
|
if (this.repository.getGroupRepository().memberExists(joinGroupTransactionData.getGroupName(), joiner.getAddress()))
|
||||||
return ValidationResult.ALREADY_GROUP_MEMBER;
|
return ValidationResult.ALREADY_GROUP_MEMBER;
|
||||||
|
|
||||||
|
// XXX Check member is not banned
|
||||||
|
|
||||||
// Check fee is positive
|
// Check fee is positive
|
||||||
if (joinGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
if (joinGroupTransactionData.getFee().compareTo(BigDecimal.ZERO) <= 0)
|
||||||
return ValidationResult.NEGATIVE_FEE;
|
return ValidationResult.NEGATIVE_FEE;
|
||||||
|
@ -8,6 +8,7 @@ import java.util.List;
|
|||||||
import org.qora.account.Account;
|
import org.qora.account.Account;
|
||||||
import org.qora.account.PublicKeyAccount;
|
import org.qora.account.PublicKeyAccount;
|
||||||
import org.qora.asset.Asset;
|
import org.qora.asset.Asset;
|
||||||
|
import org.qora.crypto.Crypto;
|
||||||
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
import org.qora.data.transaction.RemoveGroupAdminTransactionData;
|
||||||
import org.qora.data.group.GroupData;
|
import org.qora.data.group.GroupData;
|
||||||
import org.qora.data.transaction.TransactionData;
|
import org.qora.data.transaction.TransactionData;
|
||||||
@ -75,6 +76,10 @@ public class RemoveGroupAdminTransaction extends Transaction {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ValidationResult isValid() throws DataException {
|
public ValidationResult isValid() throws DataException {
|
||||||
|
// Check admin address is valid
|
||||||
|
if (!Crypto.isValidAddress(removeGroupAdminTransactionData.getAdmin()))
|
||||||
|
return ValidationResult.INVALID_ADDRESS;
|
||||||
|
|
||||||
// Check group name size bounds
|
// Check group name size bounds
|
||||||
int groupNameLength = Utf8.encodedLength(removeGroupAdminTransactionData.getGroupName());
|
int groupNameLength = Utf8.encodedLength(removeGroupAdminTransactionData.getGroupName());
|
||||||
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)
|
if (groupNameLength < 1 || groupNameLength > Group.MAX_NAME_SIZE)
|
||||||
|
@ -133,6 +133,8 @@ public abstract class Transaction {
|
|||||||
NOT_GROUP_MEMBER(53),
|
NOT_GROUP_MEMBER(53),
|
||||||
ALREADY_GROUP_ADMIN(54),
|
ALREADY_GROUP_ADMIN(54),
|
||||||
NOT_GROUP_ADMIN(55),
|
NOT_GROUP_ADMIN(55),
|
||||||
|
INVALID_LIFETIME(56),
|
||||||
|
INVITE_UNKNOWN(57),
|
||||||
NOT_YET_RELEASED(1000);
|
NOT_YET_RELEASED(1000);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
@ -244,6 +246,15 @@ public abstract class Transaction {
|
|||||||
case REMOVE_GROUP_ADMIN:
|
case REMOVE_GROUP_ADMIN:
|
||||||
return new RemoveGroupAdminTransaction(repository, transactionData);
|
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:
|
case JOIN_GROUP:
|
||||||
return new JoinGroupTransaction(repository, transactionData);
|
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:
|
case REMOVE_GROUP_ADMIN:
|
||||||
return RemoveGroupAdminTransactionTransformer.fromByteBuffer(byteBuffer);
|
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:
|
case JOIN_GROUP:
|
||||||
return JoinGroupTransactionTransformer.fromByteBuffer(byteBuffer);
|
return JoinGroupTransactionTransformer.fromByteBuffer(byteBuffer);
|
||||||
|
|
||||||
@ -185,6 +194,15 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REMOVE_GROUP_ADMIN:
|
case REMOVE_GROUP_ADMIN:
|
||||||
return RemoveGroupAdminTransactionTransformer.getDataLength(transactionData);
|
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:
|
case JOIN_GROUP:
|
||||||
return JoinGroupTransactionTransformer.getDataLength(transactionData);
|
return JoinGroupTransactionTransformer.getDataLength(transactionData);
|
||||||
|
|
||||||
@ -261,6 +279,15 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REMOVE_GROUP_ADMIN:
|
case REMOVE_GROUP_ADMIN:
|
||||||
return RemoveGroupAdminTransactionTransformer.toBytes(transactionData);
|
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:
|
case JOIN_GROUP:
|
||||||
return JoinGroupTransactionTransformer.toBytes(transactionData);
|
return JoinGroupTransactionTransformer.toBytes(transactionData);
|
||||||
|
|
||||||
@ -346,6 +373,15 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REMOVE_GROUP_ADMIN:
|
case REMOVE_GROUP_ADMIN:
|
||||||
return RemoveGroupAdminTransactionTransformer.toBytesForSigningImpl(transactionData);
|
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:
|
case JOIN_GROUP:
|
||||||
return JoinGroupTransactionTransformer.toBytesForSigningImpl(transactionData);
|
return JoinGroupTransactionTransformer.toBytesForSigningImpl(transactionData);
|
||||||
|
|
||||||
@ -443,6 +479,15 @@ public class TransactionTransformer extends Transformer {
|
|||||||
case REMOVE_GROUP_ADMIN:
|
case REMOVE_GROUP_ADMIN:
|
||||||
return RemoveGroupAdminTransactionTransformer.toJSON(transactionData);
|
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:
|
case JOIN_GROUP:
|
||||||
return JoinGroupTransactionTransformer.toJSON(transactionData);
|
return JoinGroupTransactionTransformer.toJSON(transactionData);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user