From 59119ebc3ba4c4e9f1878908c2e22fa4308a7059 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sun, 20 Mar 2022 21:59:36 +0000 Subject: [PATCH] Added GET_NAME message to allow lookups from name to owner (or any other name data). --- .../qortal/api/resource/NamesResource.java | 12 +++- .../org/qortal/controller/Controller.java | 48 ++++++++++++++++ .../java/org/qortal/controller/LiteNode.java | 19 +++++++ .../network/message/GetNameMessage.java | 55 +++++++++++++++++++ .../org/qortal/network/message/Message.java | 3 +- 5 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/qortal/network/message/GetNameMessage.java diff --git a/src/main/java/org/qortal/api/resource/NamesResource.java b/src/main/java/org/qortal/api/resource/NamesResource.java index 1b6f0bc8..a900d6bf 100644 --- a/src/main/java/org/qortal/api/resource/NamesResource.java +++ b/src/main/java/org/qortal/api/resource/NamesResource.java @@ -134,10 +134,18 @@ public class NamesResource { @ApiErrors({ApiError.NAME_UNKNOWN, ApiError.REPOSITORY_ISSUE}) public NameData getName(@PathParam("name") String name) { try (final Repository repository = RepositoryManager.getRepository()) { - NameData nameData = repository.getNameRepository().fromName(name); + NameData nameData; - if (nameData == null) + if (Settings.getInstance().isLite()) { + nameData = LiteNode.getInstance().fetchNameData(name); + } + else { + nameData = repository.getNameRepository().fromName(name); + } + + if (nameData == null) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.NAME_UNKNOWN); + } return nameData; } catch (ApiException e) { diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index ce856ab5..b6297fe2 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -209,6 +209,15 @@ public class Controller extends Thread { } public GetAccountNamesMessageStats getAccountNamesMessageStats = new GetAccountNamesMessageStats(); + public static class GetNameMessageStats { + public AtomicLong requests = new AtomicLong(); + public AtomicLong unknownAccounts = new AtomicLong(); + + public GetNameMessageStats() { + } + } + public GetNameMessageStats getNameMessageStats = new GetNameMessageStats(); + public AtomicLong latestBlocksCacheRefills = new AtomicLong(); public StatsSnapshot() { @@ -1275,6 +1284,10 @@ public class Controller extends Thread { onNetworkGetAccountNamesMessage(peer, message); break; + case GET_NAME: + onNetworkGetNameMessage(peer, message); + break; + default: LOGGER.debug(() -> String.format("Unhandled %s message [ID %d] from peer %s", message.getType().name(), message.getId(), peer)); break; @@ -1632,6 +1645,41 @@ public class Controller extends Thread { } } + private void onNetworkGetNameMessage(Peer peer, Message message) { + GetNameMessage getNameMessage = (GetNameMessage) message; + String name = getNameMessage.getName(); + this.stats.getNameMessageStats.requests.incrementAndGet(); + + try (final Repository repository = RepositoryManager.getRepository()) { + NameData nameData = repository.getNameRepository().fromName(name); + + if (nameData == null) { + // We don't have this account + this.stats.getNameMessageStats.unknownAccounts.getAndIncrement(); + + // Send valid, yet unexpected message type in response, so peer doesn't have to wait for timeout + LOGGER.debug(() -> String.format("Sending 'name unknown' response to peer %s for GET_NAME request for unknown name %s", peer, name)); + + // We'll send empty block summaries message as it's very short + Message nameUnknownMessage = new BlockSummariesMessage(Collections.emptyList()); + nameUnknownMessage.setId(message.getId()); + if (!peer.sendMessage(nameUnknownMessage)) + peer.disconnect("failed to send name-unknown response"); + return; + } + + NamesMessage namesMessage = new NamesMessage(Arrays.asList(nameData)); + namesMessage.setId(message.getId()); + + if (!peer.sendMessage(namesMessage)) { + peer.disconnect("failed to send name data"); + } + + } catch (DataException e) { + LOGGER.error(String.format("Repository issue while send name %s to peer %s", name, peer), e); + } + } + // Utilities diff --git a/src/main/java/org/qortal/controller/LiteNode.java b/src/main/java/org/qortal/controller/LiteNode.java index 8d36b414..b047d295 100644 --- a/src/main/java/org/qortal/controller/LiteNode.java +++ b/src/main/java/org/qortal/controller/LiteNode.java @@ -80,6 +80,25 @@ public class LiteNode { return namesMessage.getNameDataList(); } + /** + * Fetch info about a registered name + * @param name - the name to query + * @return a NameData object, or null if not retrieved + */ + public NameData fetchNameData(String name) { + GetNameMessage getNameMessage = new GetNameMessage(name); + NamesMessage namesMessage = (NamesMessage) this.sendMessage(getNameMessage, NAMES); + if (namesMessage == null) { + return null; + } + List nameDataList = namesMessage.getNameDataList(); + if (nameDataList == null || nameDataList.size() != 1) { + return null; + } + // We are only expecting a single item in the list + return nameDataList.get(0); + } + private Message sendMessage(Message message, MessageType expectedResponseMessageType) { // This asks a random peer for the data diff --git a/src/main/java/org/qortal/network/message/GetNameMessage.java b/src/main/java/org/qortal/network/message/GetNameMessage.java new file mode 100644 index 00000000..bdef5170 --- /dev/null +++ b/src/main/java/org/qortal/network/message/GetNameMessage.java @@ -0,0 +1,55 @@ +package org.qortal.network.message; + +import org.qortal.naming.Name; +import org.qortal.transform.TransformationException; +import org.qortal.transform.Transformer; +import org.qortal.utils.Serialization; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; + +public class GetNameMessage extends Message { + + private String name; + + public GetNameMessage(String address) { + this(-1, address); + } + + private GetNameMessage(int id, String name) { + super(id, MessageType.GET_NAME); + + this.name = name; + } + + public String getName() { + return this.name; + } + + + public static Message fromByteBuffer(int id, ByteBuffer bytes) throws UnsupportedEncodingException { + try { + String name = Serialization.deserializeSizedStringV2(bytes, Name.MAX_NAME_SIZE); + + return new GetNameMessage(id, name); + } catch (TransformationException e) { + return null; + } + } + + @Override + protected byte[] toData() { + try { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + + Serialization.serializeSizedStringV2(bytes, this.name); + + return bytes.toByteArray(); + } catch (IOException e) { + return null; + } + } + +} diff --git a/src/main/java/org/qortal/network/message/Message.java b/src/main/java/org/qortal/network/message/Message.java index f01ac8c3..c675cd96 100644 --- a/src/main/java/org/qortal/network/message/Message.java +++ b/src/main/java/org/qortal/network/message/Message.java @@ -109,7 +109,8 @@ public abstract class Message { GET_ACCOUNT_BALANCE(171), NAMES(180), - GET_ACCOUNT_NAMES(181); + GET_ACCOUNT_NAMES(181), + GET_NAME(182); public final int value; public final Method fromByteBufferMethod;