3
0
mirror of https://github.com/Qortal/qortal.git synced 2025-02-11 17:55:50 +00:00

access to decoded online accounts by block

This commit is contained in:
kennycud 2025-01-05 15:59:09 -08:00
parent 2392b7b155
commit d9a7648d36
3 changed files with 235 additions and 1 deletions

View File

@ -19,6 +19,8 @@ import org.qortal.crypto.Crypto;
import org.qortal.data.account.AccountData;
import org.qortal.data.block.BlockData;
import org.qortal.data.block.BlockSummaryData;
import org.qortal.data.block.DecodedOnlineAccountData;
import org.qortal.data.network.OnlineAccountData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.repository.BlockArchiveReader;
import org.qortal.repository.DataException;
@ -27,6 +29,7 @@ import org.qortal.repository.RepositoryManager;
import org.qortal.transform.TransformationException;
import org.qortal.transform.block.BlockTransformer;
import org.qortal.utils.Base58;
import org.qortal.utils.Blocks;
import org.qortal.utils.Triple;
import javax.servlet.http.HttpServletRequest;
@ -45,6 +48,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
@Path("/blocks")
@Tag(name = "Blocks")
@ -889,4 +893,50 @@ public class BlocksResource {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
}
@GET
@Path("/onlineaccounts/{height}")
@Operation(
summary = "Get online accounts for block",
description = "Returns the online accounts who submitted signatures for this block",
responses = {
@ApiResponse(
description = "online accounts",
content = @Content(
array = @ArraySchema(
schema = @Schema(
implementation = DecodedOnlineAccountData.class
)
)
)
)
}
)
@ApiErrors({
ApiError.BLOCK_UNKNOWN, ApiError.REPOSITORY_ISSUE
})
public Set<DecodedOnlineAccountData> getOnlineAccounts(@PathParam("height") int height) {
try (final Repository repository = RepositoryManager.getRepository()) {
// get block from database
BlockData blockData = repository.getBlockRepository().fromHeight(height);
// if block data is not in the database, then try the archive
if (blockData == null) {
blockData = repository.getBlockArchiveRepository().fromHeight(height);
// if the block is not in the database or the archive, then the block is unknown
if( blockData == null ) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.BLOCK_UNKNOWN);
}
}
Set<DecodedOnlineAccountData> onlineAccounts = Blocks.getDecodedOnlineAccountsForBlock(repository, blockData);
return onlineAccounts;
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE);
}
}
}

View File

@ -0,0 +1,85 @@
package org.qortal.data.block;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import java.util.Objects;
// All properties to be converted to JSON via JAX-RS
@XmlAccessorType(XmlAccessType.FIELD)
public class DecodedOnlineAccountData {
private long onlineTimestamp;
private String minter;
private String recipient;
private int sharePercent;
private boolean minterGroupMember;
private String name;
private int level;
public DecodedOnlineAccountData() {
}
public DecodedOnlineAccountData(long onlineTimestamp, String minter, String recipient, int sharePercent, boolean minterGroupMember, String name, int level) {
this.onlineTimestamp = onlineTimestamp;
this.minter = minter;
this.recipient = recipient;
this.sharePercent = sharePercent;
this.minterGroupMember = minterGroupMember;
this.name = name;
this.level = level;
}
public long getOnlineTimestamp() {
return onlineTimestamp;
}
public String getMinter() {
return minter;
}
public String getRecipient() {
return recipient;
}
public int getSharePercent() {
return sharePercent;
}
public boolean isMinterGroupMember() {
return minterGroupMember;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
DecodedOnlineAccountData that = (DecodedOnlineAccountData) o;
return onlineTimestamp == that.onlineTimestamp && sharePercent == that.sharePercent && minterGroupMember == that.minterGroupMember && level == that.level && Objects.equals(minter, that.minter) && Objects.equals(recipient, that.recipient) && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(onlineTimestamp, minter, recipient, sharePercent, minterGroupMember, name, level);
}
@Override
public String toString() {
return "DecodedOnlineAccountData{" +
"onlineTimestamp=" + onlineTimestamp +
", minter='" + minter + '\'' +
", recipient='" + recipient + '\'' +
", sharePercent=" + sharePercent +
", minterGroupMember=" + minterGroupMember +
", name='" + name + '\'' +
", level=" + level +
'}';
}
}

View File

@ -0,0 +1,99 @@
package org.qortal.utils;
import io.druid.extendedset.intset.ConciseSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.block.BlockChain;
import org.qortal.data.account.AddressLevelPairing;
import org.qortal.data.account.RewardShareData;
import org.qortal.data.block.BlockData;
import org.qortal.data.block.DecodedOnlineAccountData;
import org.qortal.data.group.GroupMemberData;
import org.qortal.data.naming.NameData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.transform.block.BlockTransformer;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Class Blocks
*
* Methods for block related logic.
*/
public class Blocks {
private static final Logger LOGGER = LogManager.getLogger(Blocks.class);
/**
* Get Decode Online Accounts For Block
*
* @param repository the data repository
* @param blockData the block data
*
* @return the online accounts set to the block
*
* @throws DataException
*/
public static Set<DecodedOnlineAccountData> getDecodedOnlineAccountsForBlock(Repository repository, BlockData blockData) throws DataException {
try {
// get all online account indices from block
ConciseSet onlineAccountIndices = BlockTransformer.decodeOnlineAccounts(blockData.getEncodedOnlineAccounts());
// get online reward shares from the online accounts on the block
List<RewardShareData> onlineRewardShares = repository.getAccountRepository().getRewardSharesByIndexes(onlineAccountIndices.toArray());
// online timestamp for block
long onlineTimestamp = blockData.getOnlineAccountsTimestamp();
Set<DecodedOnlineAccountData> onlineAccounts = new HashSet<>();
// all minting group member addresses
List<String> mintingGroupAddresses
= repository.getGroupRepository()
.getGroupMembers(BlockChain.getInstance().getMintingGroupId()).stream()
.map(GroupMemberData::getMember)
.collect(Collectors.toList());
// all names, indexed by address
Map<String, String> nameByAddress
= repository.getNameRepository()
.getAllNames().stream()
.collect(Collectors.toMap(NameData::getOwner, NameData::getName));
// all accounts at level 1 or higher, indexed by address
Map<String, Integer> levelByAddress
= repository.getAccountRepository().getAddressLevelPairings(1).stream()
.collect(Collectors.toMap(AddressLevelPairing::getAddress, AddressLevelPairing::getLevel));
// for each reward share where the minter is online,
// construct the data object and add it to the return list
for (RewardShareData onlineRewardShare : onlineRewardShares) {
String minter = onlineRewardShare.getMinter();
DecodedOnlineAccountData onlineAccountData
= new DecodedOnlineAccountData(
onlineTimestamp,
minter,
onlineRewardShare.getRecipient(),
onlineRewardShare.getSharePercent(),
mintingGroupAddresses.contains(minter),
nameByAddress.get(minter),
levelByAddress.get(minter)
);
onlineAccounts.add(onlineAccountData);
}
return onlineAccounts;
} catch (DataException e) {
throw e;
} catch (Exception e ) {
LOGGER.error(e.getMessage(), e);
return new HashSet<>(0);
}
}
}