diff --git a/.gitignore b/.gitignore index adbbce4..1973b49 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # # Ignore project resources +no2all-zerofungus/logs # Ignore maven resources target diff --git a/README.md b/README.md index 3dea775..a55574e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # No2All -THIS WIP AND NEVER BEEN RUN. +WIP-NOTE; Never been started as only 20% is done. -Send signed notes to all, is a java library to work with the nostr protocol. -And zerofungus moves all packages managers over two blockchains. +Send signed notes to all, no2all-nostr is a POJO library to work +with the nostr protocol. And no2all-zerofungus is experimental server +to move all package managers content in DAT and signed in nostr.. TODO: With java webstart example over DAT protocol. This wowk though dewey of UwU is done in toe toe twailing chains. @@ -28,8 +29,9 @@ todo - NIP-X: Add "nsecpw" password protected private key in bech32 TLV format - NIP-X: Add "nssurl" bech32 TLV for encoded url+pubkey+pubvals+?sockey - NIP-X: Add "nsseed" web of trust voting(plus/min) + policy tree trust for revoke, like running npkg apps -- NIP-X: Add "npkgurl" Universal pkg, seed msg type to relay + upkeep + replopt -- NIP-X: Redo/Clone NIP-89 appurl handlers +- NIP-X: Add "npkgurl" Universal pkg(+refs) of mime types, seed msg type to relay + upkeep + replopt +- NIP-X: Add new WS messages for relay managed writable dat streams (after client auth) +- NIP-X: Redo/Rm/Clone NIP-89 appurl handlers ## ZeroFunGus @@ -59,3 +61,4 @@ This will run; - Optional create native OS menu entry for application - Run jvm with jars and parameters from jnlp file - Welcome to a js free world over 2 blockchains +- Also called "The red sea file system" to boot an TempleOS instance diff --git a/no2all-nostr/pom.xml b/no2all-nostr/pom.xml new file mode 100644 index 0000000..0537620 --- /dev/null +++ b/no2all-nostr/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-nostr + No2All-NoStr + + + ${project.groupId} + no2all-octo + + + jakarta.json + jakarta.json-api + + + \ No newline at end of file diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/NoStr.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/NoStr.java new file mode 100644 index 0000000..97566b7 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/NoStr.java @@ -0,0 +1,25 @@ +package love.distributedrebirth.no2all.nostr; + +import java.util.function.Consumer; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireWaterBoiler; +import love.distributedrebirth.no2all.nostr.fire.NoStrFirePipe; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentity; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClient; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelay; + +public enum NoStr { + FACTORY; + + public NoStrFireWaterBoiler boiler(NoStrIdentity identity) { + return new NoStrFireWaterBoiler(identity); + } + + public NoStrFirePipe pipeReaderClient(Consumer errHandler, Consumer msgHandler) { + return new NoStrFirePipe(errHandler, null, msgHandler); + } + + public NoStrFirePipe pipeReaderRelay(Consumer errHandler, Consumer msgHandler) { + return new NoStrFirePipe(errHandler, msgHandler, null); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/NoStrGospel.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/NoStrGospel.java new file mode 100644 index 0000000..3e37b98 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/NoStrGospel.java @@ -0,0 +1,9 @@ +package love.distributedrebirth.no2all.nostr; + +import jakarta.json.JsonValue; + +public interface NoStrGospel { + + // slow version, add perf test, rewrite to streaming mode + JsonValue toBible(); +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFirePipe.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFirePipe.java new file mode 100644 index 0000000..80bf4b1 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFirePipe.java @@ -0,0 +1,72 @@ +package love.distributedrebirth.no2all.nostr.fire; + +import java.io.StringReader; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonReader; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClient; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelay; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrFirePipe { + private static final String ERROR_INVALID_JSON = "Invalid json; "; // on json parse + private static final String ERROR_INVALID_ROOT = "Invalid root; "; // on none array root + private static final String ERROR_TYPE_UNKNOWN = "Unknown type; "; // on not in values() + private static final String ERROR_TYPE_ILLEGAL = "Illegal type"; // on null mapper + private static final int MSG_TYPE_INDEX = 0; + private final Consumer errHandler; + private final Consumer msgHandlerToRelay; + private final Consumer msgHandlerToClient; + + public NoStrFirePipe(Consumer errHandler, Consumer msgHandlerToRelay, + Consumer msgHandlerToClient) { + this.errHandler = Objects.requireNonNull(errHandler); + this.msgHandlerToRelay = msgHandlerToRelay; + this.msgHandlerToClient = msgHandlerToClient; + if (msgHandlerToRelay != null && msgHandlerToClient != null) { + throw new IllegalArgumentException("Only use one message handler"); + } + if (msgHandlerToRelay == null && msgHandlerToClient == null) { + throw new NullPointerException("Need one message handler"); + } + } + + public void onFire(String msgStr) { + JsonValue msgJson = null; + try (JsonReader jsonReader = Json.createReader(new StringReader(msgStr))) { + msgJson = jsonReader.readValue(); + } catch (Exception e) { + errHandler.accept(ERROR_INVALID_JSON + e.getMessage()); + return; + } + if (!JsonValue.ValueType.ARRAY.equals(msgJson.getValueType())) { + errHandler.accept(ERROR_INVALID_ROOT + msgJson.getValueType().name()); + return; + } + JsonArray msgPacket = msgJson.asJsonArray(); + String msgTypeRaw = msgPacket.getString(MSG_TYPE_INDEX); + if (!NoStrImplMessageType.valueOfValid(msgTypeRaw)) { + errHandler.accept(ERROR_TYPE_UNKNOWN + msgTypeRaw); + return; + } + NoStrImplMessageType msgType = NoStrImplMessageType.valueOf(msgTypeRaw); + if (msgHandlerToClient != null) { + acceptType(msgType.getMapperToClient(), v -> msgHandlerToClient.accept(v.apply(msgPacket))); // on relay + } else { + acceptType(msgType.getMapperToRelay(), v -> msgHandlerToRelay.accept(v.apply(msgPacket))); // on client + } + } + + private void acceptType(Optional mapper, Consumer messageHandler) { + if (mapper.isEmpty()) { + errHandler.accept(ERROR_TYPE_ILLEGAL); + return; + } + messageHandler.accept(mapper.get()); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireStackFishWelder.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireStackFishWelder.java new file mode 100644 index 0000000..3e686ba --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireStackFishWelder.java @@ -0,0 +1,8 @@ +package love.distributedrebirth.no2all.nostr.fire; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTag; + +public interface NoStrFireStackFishWelder { + + NoStrFireStackWelder tag(NoStrEventTag eventTag); +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireStackWelder.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireStackWelder.java new file mode 100644 index 0000000..77e24b8 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireStackWelder.java @@ -0,0 +1,76 @@ +package love.distributedrebirth.no2all.nostr.fire; + +import java.util.List; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTag; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagMeta; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagA; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagAlt; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagClient; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagContentWarning; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagD; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagDescription; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagE; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagI; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagImage; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagName; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagNonce; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagP; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagPublishedAt; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagR; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagSubject; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagSummary; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagT; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagThumb; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagTitle; + +public class NoStrFireStackWelder implements NoStrFireStackFishWelder, + NoStrTagA.FishWelder, + NoStrTagAlt.FishWelder, + NoStrTagClient.FishWelder, + NoStrTagContentWarning.FishWelder, + NoStrTagD.FishWelder, + NoStrTagDescription.FishWelder, + NoStrTagE.FishWelder, + NoStrTagI.FishWelder, + NoStrTagImage.FishWelder, + NoStrTagName.FishWelder, + NoStrTagNonce.FishWelder, + NoStrTagP.FishWelder, + NoStrTagPublishedAt.FishWelder, + NoStrTagR.FishWelder, + NoStrTagSubject.FishWelder, + NoStrTagSummary.FishWelder, + NoStrTagT.FishWelder, + NoStrTagThumb.FishWelder, + NoStrTagTitle.FishWelder +{ + private final NoStrFireWaterWelder welder; + + protected NoStrFireStackWelder(NoStrFireWaterWelder welder) { + this.welder = welder; + } + + public NoStrFireWaterWelder burn() { + return welder; + } + + @Override + public NoStrFireStackWelder tag(NoStrEventTag eventTag) { + welder.smokeTag(eventTag); + return this; + } + + public NoStrFireStackWelder tags(List eventTags) { + eventTags.forEach(v -> welder.smokeTag(v)); + return this; + } + + public NoStrFireStackWelder tag(NoStrEventTagMeta tag, String... args) { + return tag(NoStrEventTag.valueOf(tag, args)); + } + + public NoStrFireStackWelder tag(String tag, String... args) { + return tag(NoStrEventTagMeta.valueOf(tag), args); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireWaterBoiler.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireWaterBoiler.java new file mode 100644 index 0000000..f93d5fe --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireWaterBoiler.java @@ -0,0 +1,62 @@ +package love.distributedrebirth.no2all.nostr.fire; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentity; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.model.NoStrProfileMetaData; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagP; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventKind; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrFireWaterBoiler { + + private final NoStrIdentity identity; + + public NoStrFireWaterBoiler(NoStrIdentity identity) { + this.identity = Objects.requireNonNull(identity); + } + + public NoStrFireWaterWelder burn() { + return new NoStrFireWaterWelder(identity); + } + + public NoStrFireStackWelder smoker() { + return new NoStrFireStackWelder(burn()); + } + + public NoStrEvent boilProfileMetaData(NoStrProfileMetaData profileData) { + return burn().water(NoStrImplEventKind.METADATA, profileData.toBible().toString()); + } + + public NoStrEvent boilClientAuth(String relay, String challenge) { + return smoker() + .tag(NoStrImplEventTag.RELAY, relay) + .tag(NoStrImplEventTag.CHALLENGE, challenge) + .burn() + .water(NoStrImplEventKind.CLIENT_AUTH); + } + + public NoStrEvent boilRecommendRelay(String relayUrl) { + return burn().water(NoStrImplEventKind.RECOMMEND_RELAY, relayUrl); + } + + public NoStrEvent boilTextNote(String content, String subject, List pubs) { + return smoker() + .tagSubject(subject) + .tags(pubs.stream().map(v -> new NoStrTagP(v, Optional.empty(), Optional.empty())).collect(Collectors.toList())) + .burn() + .water(NoStrImplEventKind.TEXT_NOTE, content); + } + +// public NoStrEvent waterTextNote(String content, List eTags, List pubs) { +// return smoker() +// .tag(NoStrImplEventTag.P, pubs.stream().map(v -> v.getHex()).collect(Collectors.toList())) +// .weld() +// .water(NoStrImplEventKind.TEXT_NOTE, content); +// } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireWaterWelder.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireWaterWelder.java new file mode 100644 index 0000000..72b5362 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/fire/NoStrFireWaterWelder.java @@ -0,0 +1,70 @@ +package love.distributedrebirth.no2all.nostr.fire; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentity; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKind; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventPayload; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventSignature; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTag; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagMeta; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagClient; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventKind; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public class NoStrFireWaterWelder { + + private final List smokeTags = new ArrayList<>(); + private final NoStrIdentity identity; + + public NoStrFireWaterWelder(NoStrIdentity identity) { + this.identity = Objects.requireNonNull(identity); + } + + public NoStrFireWaterWelder smokeTag(NoStrEventTag tag) { + smokeTags.add(Objects.requireNonNull(tag)); + return this; + } + + private List findByMeta(NoStrEventTagMeta meta) { + List result = new ArrayList<>(); + for (NoStrEventTag tag : smokeTags) { + if (tag.getMeta().getQName().equals(meta.getQName())) { + result.add(tag); + } + } + return result; + } + + private void waterFishNet(NoStrEventKind kind) { + + // TODO: here ? and on which kinds ALL ? + if (NoStrImplEventKind.TEXT_NOTE.equals(kind)) { + if (findByMeta(NoStrImplEventTag.CLIENT).isEmpty()) { + smokeTags.add(new NoStrTagClient("No2All", Optional.empty())); + } + } + // validate per kind minimal required tags here ? + } + + public NoStrEvent water(NoStrEventKind kind, String content) { + waterFishNet(kind); + Instant wave = Instant.now(); + NoStrEventPayload payload = new NoStrEventPayload(identity.getPublicKey(), wave, kind, smokeTags, content); + return NoStrEventSignature.sign(payload, identity.getPrivateKey()); + } + + public NoStrEvent water(NoStrEventKind kind) { + return water(kind, ""); + } + + public NoStrEvent water(NoStrEventKind kind, String content, List tags) { + this.smokeTags.addAll(tags); + return water(kind, content); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentity.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentity.java new file mode 100644 index 0000000..d3427b4 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentity.java @@ -0,0 +1,22 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.Objects; + +public final class NoStrIdentity { + + private final NoStrIdentityPrivateKey privateKey; + private final NoStrIdentityPublicKey publicKey; + + public NoStrIdentity(NoStrIdentityPrivateKey privateKey) { + this.privateKey = Objects.requireNonNull(privateKey); + this.publicKey = NoStrIdentityPublicKey.of(privateKey); + } + + public NoStrIdentityPrivateKey getPrivateKey() { + return privateKey; + } + + public NoStrIdentityPublicKey getPublicKey() { + return publicKey; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityPrivateKey.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityPrivateKey.java new file mode 100644 index 0000000..5630fdf --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityPrivateKey.java @@ -0,0 +1,24 @@ +package love.distributedrebirth.no2all.nostr.model; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplBinHexAdapter; +import love.distributedrebirth.no2all.octo.OctoBitFormat; +import love.distributedrebirth.no2all.octo.trust.OctoTrust; +import love.distributedrebirth.no2all.octo.trust.OctoTrustException; + +public final class NoStrIdentityPrivateKey extends NoStrImplBinHexAdapter { + + public NoStrIdentityPrivateKey(byte[] keyBytes) { + super(keyBytes); + if (keyBytes.length != OctoTrust.KEY_LENGTH) { + throw new OctoTrustException("Key length is not: " + OctoTrust.KEY_LENGTH); + } + } + + public static NoStrIdentityPrivateKey ofRandom() { + return new NoStrIdentityPrivateKey(OctoTrust.generateSecretKey()); + } + + public static NoStrIdentityPrivateKey ofHex(String keyHex) { + return new NoStrIdentityPrivateKey(OctoBitFormat.HEX.toBytes(keyHex)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityPublicKey.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityPublicKey.java new file mode 100644 index 0000000..249ee08 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityPublicKey.java @@ -0,0 +1,25 @@ +package love.distributedrebirth.no2all.nostr.model; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplBinHexAdapter; +import love.distributedrebirth.no2all.octo.OctoBitFormat; +import love.distributedrebirth.no2all.octo.trust.OctoTrust; +import love.distributedrebirth.no2all.octo.trust.OctoTrustException; +import love.distributedrebirth.no2all.octo.trust.OctoTrustSchnorr; + +public final class NoStrIdentityPublicKey extends NoStrImplBinHexAdapter { + + public NoStrIdentityPublicKey(byte[] keyBytes) { + super(keyBytes); + if (keyBytes.length != OctoTrust.KEY_LENGTH) { + throw new OctoTrustException("Key length is not: " + OctoTrust.KEY_LENGTH); + } + } + + public static NoStrIdentityPublicKey ofHex(String keyHex) { + return new NoStrIdentityPublicKey(OctoBitFormat.HEX.toBytes(keyHex)); + } + + public static NoStrIdentityPublicKey of(NoStrIdentityPrivateKey privateKey) { + return new NoStrIdentityPublicKey(OctoTrustSchnorr.extractPublicKey(privateKey.getBytes())); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrKindValue.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrKindValue.java new file mode 100644 index 0000000..4ccd3fd --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrKindValue.java @@ -0,0 +1,77 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.List; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplChuch; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public final class NoStrKindValue { + + private final int kind; + private final int kindStopRange; + + public NoStrKindValue(int kind) { + this(kind, -1); + } + + public NoStrKindValue(int kind, int kindStopRange) { + this.kind = kind; + this.kindStopRange = kindStopRange; + } + + public int getKind() { + return kind; + } + + public int getKindStopRange() { + return kindStopRange; + } + + public boolean isRange() { + return kindStopRange != -1; + } + + public static void readArrayKinds(JsonObject json, NoStrImplQName jsonName, List data) { + readArrayKinds(json, jsonName.getQName(), data); + } + + public static void readArrayKinds(JsonObject json, String jsonName, List data) { + if (!json.containsKey(jsonName)) { + return; + } + JsonArray values = json.getJsonArray(jsonName); + for (int i = 0; i < values.size(); i++) { + JsonValue value = values.get(i); + if (JsonValue.ValueType.ARRAY.equals(value.getValueType())) { + JsonArray arr = value.asJsonArray(); + int v1 = arr.getInt(0); + int v2 = arr.getInt(1); + data.add(new NoStrKindValue(v1, v2)); + } else { + data.add(new NoStrKindValue(values.getInt(i))); + } + } + } + + public static void writeArrayKinds(JsonObjectBuilder json, NoStrImplQName jsonName, List data) { + writeArrayKinds(json, jsonName.getQName(), data); + } + + public static void writeArrayKinds(JsonObjectBuilder json, String jsonName, List data) { + NoStrImplChuch.writeArray(json, jsonName, data, (r, v) -> { + if (v.isRange()) { + r.add(Json.createArrayBuilder() + .add(v.getKind()) + .add(v.getKindStopRange()) + .build()); + } else { + r.add(v.getKind()); + } + }); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrProfileMetaData.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrProfileMetaData.java new file mode 100644 index 0000000..4a80de1 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrProfileMetaData.java @@ -0,0 +1,121 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.HashMap; +import java.util.Map; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public final class NoStrProfileMetaData implements NoStrGospel { + + private final Map fields = new HashMap<>(); + + public NoStrProfileMetaData() { + } + + public NoStrProfileMetaData(JsonObject value) { + for (String fieldName : value.keySet()) { + JsonValue fieldValue = value.get(fieldName); + if (fieldValue instanceof JsonString) { + fields.put(fieldName, ((JsonString)fieldValue).getString()); + } + } + } + + @Override + public JsonValue toBible() { + return Json.createObjectBuilder(fields).build(); + } + + private enum QName implements NoStrImplQName { + + NAME, + DISPLAY_NAME, + ABOUT, + WEBSITE, + PICTURE, + BANNER, + NIP05, + LUD16 + ; + + private final String jsonQName; + + private QName() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + } + + public String getName() { + return fields.get(QName.NAME.getQName()); + } + + public void setName(String name) { + this.fields.put(QName.NAME.getQName(), name); + } + + public String getDisplayName() { + return fields.get(QName.DISPLAY_NAME.getQName()); + } + + public void setDisplayName(String displayName) { + this.fields.put(QName.DISPLAY_NAME.getQName(), displayName); + } + + public String getAbout() { + return fields.get(QName.ABOUT.getQName()); + } + + public void setAbout(String about) { + this.fields.put(QName.ABOUT.getQName(), about); + } + + public String getWebsite() { + return fields.get(QName.WEBSITE.getQName()); + } + + public void setWebsite(String website) { + this.fields.put(QName.WEBSITE.getQName(), website); + } + + public String getPicture() { + return fields.get(QName.PICTURE.getQName()); + } + + public void setPicture(String picture) { + this.fields.put(QName.PICTURE.getQName(), picture); + } + + public String getBanner() { + return fields.get(QName.BANNER.getQName()); + } + + public void setBanner(String banner) { + this.fields.put(QName.BANNER.getQName(), banner); + } + + public String getNip05() { + return fields.get(QName.NIP05.getQName()); + } + + public void setNip05(String nip05) { + this.fields.put(QName.NIP05.getQName(), nip05); + } + + public String getLud16() { + return fields.get(QName.LUD16.getQName()); + } + + public void setLud16(String lud16) { + this.fields.put(QName.LUD16.getQName(), lud16); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfo.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfo.java new file mode 100644 index 0000000..cfd7b38 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfo.java @@ -0,0 +1,250 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplChuch; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public final class NoStrRelayInfo implements NoStrGospel { + + private String id; + private String name; + private String description; + private String publicKey; + private String contact; + private final List supportedNips = new ArrayList<>(); + private String software; + private String version; + + private NoStrRelayInfoLimitation limitation; + private final List retentions = new ArrayList<>(); + + private final List relayCountries = new ArrayList<>(); + private final List languageTags = new ArrayList<>(); + private final List tags = new ArrayList<>(); + private String postingPolicy; + + private String paymentUrl; + private final List feesAdmission = new ArrayList<>(); + private final List feesSubscription = new ArrayList<>(); + private final List feesPublication = new ArrayList<>(); + + public NoStrRelayInfo() { + } + + public NoStrRelayInfo(JsonObject json) { + id = NoStrImplChuch.readString(json, QName.ID); + name = NoStrImplChuch.readString(json, QName.NAME); + description = NoStrImplChuch.readString(json, QName.DESCRIPTION); + publicKey = NoStrImplChuch.readString(json, QName.PUBKEY); + contact = NoStrImplChuch.readString(json, QName.CONTACT); + if (json.containsKey(QName.SUPPORTED_NIPS.getQName())) { + JsonArray values = json.getJsonArray(QName.SUPPORTED_NIPS.getQName()); + for (int i = 0; i < values.size(); i++) { + supportedNips.add(values.getInt(i)); + } + } + software = NoStrImplChuch.readString(json, QName.SOFTWARE); + version = NoStrImplChuch.readString(json, QName.VERSION); + JsonValue limitationJson = json.get(QName.LIMITATION.getQName()); + if (limitationJson != null && JsonValue.ValueType.OBJECT.equals(limitationJson.getValueType())) { + limitation = new NoStrRelayInfoLimitation(limitationJson.asJsonObject()); + } + NoStrImplChuch.readArray(json, QName.RETENTION, v -> retentions.add(new NoStrRelayInfoRetention(v.asJsonObject()))); + NoStrImplChuch.readArrayString(json, QName.RELAY_COUNTRIES, relayCountries); + NoStrImplChuch.readArrayString(json, QName.LANGUAGE_TAGS, languageTags); + NoStrImplChuch.readArrayString(json, QName.TAGS, tags); + postingPolicy = NoStrImplChuch.readString(json, QName.POSTING_POLICY); + paymentUrl = NoStrImplChuch.readString(json, QName.PAYMENT_URL); + JsonValue feesJson = json.get(QName.FEES.getQName()); + if (feesJson != null && JsonValue.ValueType.OBJECT.equals(feesJson.getValueType())) { + JsonObject feesObject = feesJson.asJsonObject(); + NoStrImplChuch.readArray(feesObject, QName.ADMISSION, v -> feesAdmission.add(new NoStrRelayInfoPayment(v.asJsonObject()))); + NoStrImplChuch.readArray(feesObject, QName.SUBSCRIPTION, v -> feesSubscription.add(new NoStrRelayInfoPayment(v.asJsonObject()))); + NoStrImplChuch.readArray(feesObject, QName.PUBLICATION, v -> feesPublication.add(new NoStrRelayInfoPayment(v.asJsonObject()))); + } + } + + @Override + public JsonValue toBible() { + JsonObjectBuilder result = Json.createObjectBuilder(); + NoStrImplChuch.writeString(result, QName.ID, id); + NoStrImplChuch.writeString(result, QName.NAME, name); + NoStrImplChuch.writeString(result, QName.DESCRIPTION, description); + NoStrImplChuch.writeString(result, QName.PUBKEY, publicKey); + NoStrImplChuch.writeString(result, QName.CONTACT, contact); + NoStrImplChuch.writeArray(result, QName.SUPPORTED_NIPS, supportedNips, (r, v) -> r.add(v)); + NoStrImplChuch.writeString(result, QName.SOFTWARE, software); + NoStrImplChuch.writeString(result, QName.VERSION, version); + if (limitation != null) { + result.add(QName.LIMITATION.getQName(), limitation.toBible()); + } + NoStrImplChuch.writeArrayWriter(result, QName.RETENTION, retentions); + NoStrImplChuch.writeArrayString(result, QName.RELAY_COUNTRIES, relayCountries); + NoStrImplChuch.writeArrayString(result, QName.LANGUAGE_TAGS, languageTags); + NoStrImplChuch.writeArrayString(result, QName.TAGS, tags); + NoStrImplChuch.writeString(result, QName.POSTING_POLICY, postingPolicy); + NoStrImplChuch.writeString(result, QName.PAYMENT_URL, paymentUrl); + + JsonObjectBuilder feesObject = Json.createObjectBuilder(); + NoStrImplChuch.writeArrayWriter(feesObject, QName.ADMISSION, feesAdmission); + NoStrImplChuch.writeArrayWriter(feesObject, QName.SUBSCRIPTION, feesSubscription); + NoStrImplChuch.writeArrayWriter(feesObject, QName.PUBLICATION, feesPublication); + result.add(QName.FEES.getQName(), feesObject); + return result.build(); + } + + private enum QName implements NoStrImplQName { + + ID, + NAME, + DESCRIPTION, + PUBKEY, + CONTACT, + SUPPORTED_NIPS, + SOFTWARE, + VERSION, + LIMITATION, + RETENTION, + RELAY_COUNTRIES, + LANGUAGE_TAGS, + TAGS, + POSTING_POLICY, + PAYMENT_URL, + ADMISSION, + SUBSCRIPTION, + PUBLICATION, + FEES, + ; + + private final String jsonQName; + + private QName() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; + } + + public String getContact() { + return contact; + } + + public void setContact(String contact) { + this.contact = contact; + } + + public String getSoftware() { + return software; + } + + public void setSoftware(String software) { + this.software = software; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public NoStrRelayInfoLimitation getLimitation() { + return limitation; + } + + public void setLimitation(NoStrRelayInfoLimitation limitation) { + this.limitation = limitation; + } + + public String getPostingPolicy() { + return postingPolicy; + } + + public void setPostingPolicy(String postingPolicy) { + this.postingPolicy = postingPolicy; + } + + public String getPaymentUrl() { + return paymentUrl; + } + + public void setPaymentUrl(String paymentUrl) { + this.paymentUrl = paymentUrl; + } + + public List getSupportedNips() { + return supportedNips; + } + + public List getRetentions() { + return retentions; + } + + public List getRelayCountries() { + return relayCountries; + } + + public List getLanguageTags() { + return languageTags; + } + + public List getTags() { + return tags; + } + + public List getFeesAdmission() { + return feesAdmission; + } + + public List getFeesSubscription() { + return feesSubscription; + } + + public List getFeesPublication() { + return feesPublication; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoLimitation.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoLimitation.java new file mode 100644 index 0000000..5d8c5d3 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoLimitation.java @@ -0,0 +1,173 @@ +package love.distributedrebirth.no2all.nostr.model; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplChuch; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public final class NoStrRelayInfoLimitation implements NoStrGospel { + + private Integer maxMessageLength; + private Integer maxSubscriptions; + private Integer maxFilters; + private Integer maxLimit; + private Integer maxSubIdLength; + private Integer minPrefix; + private Integer maxEventTags; + private Integer maxContentLength; + private Integer minPowDifficulty; + private Boolean authRequired; + private Boolean paymentRequired; + + public NoStrRelayInfoLimitation() { + } + + public NoStrRelayInfoLimitation(JsonObject json) { + maxMessageLength = NoStrImplChuch.readInteger(json, QName.MAX_MESSAGE_LENGTH); + maxSubscriptions = NoStrImplChuch.readInteger(json, QName.MAX_SUBSCRIPTIONS); + maxFilters = NoStrImplChuch.readInteger(json, QName.MAX_FILTERS); + maxLimit = NoStrImplChuch.readInteger(json, QName.MAX_LIMIT); + maxSubIdLength = NoStrImplChuch.readInteger(json, QName.MAX_SUBID_LENGTH); + minPrefix = NoStrImplChuch.readInteger(json, QName.MIN_PREFIX); + maxEventTags = NoStrImplChuch.readInteger(json, QName.MAX_EVENT_TAGS); + maxContentLength = NoStrImplChuch.readInteger(json, QName.MAX_CONTENT_LENGTH); + minPowDifficulty = NoStrImplChuch.readInteger(json, QName.MIN_POW_DIFFICULTY); + authRequired = NoStrImplChuch.readBoolean(json, QName.AUTH_REQUIRED); + paymentRequired = NoStrImplChuch.readBoolean(json, QName.PAYMENT_REQUIRED); + } + + @Override + public JsonValue toBible() { + JsonObjectBuilder result = Json.createObjectBuilder(); + NoStrImplChuch.writeInteger(result, QName.MAX_MESSAGE_LENGTH, maxMessageLength); + NoStrImplChuch.writeInteger(result, QName.MAX_SUBSCRIPTIONS, maxSubscriptions); + NoStrImplChuch.writeInteger(result, QName.MAX_FILTERS, maxFilters); + NoStrImplChuch.writeInteger(result, QName.MAX_LIMIT, maxLimit); + NoStrImplChuch.writeInteger(result, QName.MAX_SUBID_LENGTH, maxSubIdLength); + NoStrImplChuch.writeInteger(result, QName.MIN_PREFIX, minPrefix); + NoStrImplChuch.writeInteger(result, QName.MAX_EVENT_TAGS, maxEventTags); + NoStrImplChuch.writeInteger(result, QName.MAX_CONTENT_LENGTH, maxContentLength); + NoStrImplChuch.writeInteger(result, QName.MIN_POW_DIFFICULTY, minPowDifficulty); + NoStrImplChuch.writeBoolean(result, QName.AUTH_REQUIRED, authRequired); + NoStrImplChuch.writeBoolean(result, QName.PAYMENT_REQUIRED, paymentRequired); + return result.build(); + } + + private enum QName implements NoStrImplQName { + + MAX_MESSAGE_LENGTH, + MAX_SUBSCRIPTIONS, + MAX_FILTERS, + MAX_LIMIT, + MAX_SUBID_LENGTH, + MIN_PREFIX, + MAX_EVENT_TAGS, + MAX_CONTENT_LENGTH, + MIN_POW_DIFFICULTY, + AUTH_REQUIRED, + PAYMENT_REQUIRED, + ; + + private final String jsonQName; + + private QName() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + } + + public Integer getMaxMessageLength() { + return maxMessageLength; + } + + public void setMaxMessageLength(Integer maxMessageLength) { + this.maxMessageLength = maxMessageLength; + } + + public Integer getMaxSubscriptions() { + return maxSubscriptions; + } + + public void setMaxSubscriptions(Integer maxSubscriptions) { + this.maxSubscriptions = maxSubscriptions; + } + + public Integer getMaxFilters() { + return maxFilters; + } + + public void setMaxFilters(Integer maxFilters) { + this.maxFilters = maxFilters; + } + + public Integer getMaxLimit() { + return maxLimit; + } + + public void setMaxLimit(Integer maxLimit) { + this.maxLimit = maxLimit; + } + + public Integer getMaxSubIdLength() { + return maxSubIdLength; + } + + public void setMaxSubIdLength(Integer maxSubIdLength) { + this.maxSubIdLength = maxSubIdLength; + } + + public Integer getMinPrefix() { + return minPrefix; + } + + public void setMinPrefix(Integer minPrefix) { + this.minPrefix = minPrefix; + } + + public Integer getMaxEventTags() { + return maxEventTags; + } + + public void setMaxEventTags(Integer maxEventTags) { + this.maxEventTags = maxEventTags; + } + + public Integer getMaxContentLength() { + return maxContentLength; + } + + public void setMaxContentLength(Integer maxContentLength) { + this.maxContentLength = maxContentLength; + } + + public Integer getMinPowDifficulty() { + return minPowDifficulty; + } + + public void setMinPowDifficulty(Integer minPowDifficulty) { + this.minPowDifficulty = minPowDifficulty; + } + + public Boolean getAuthRequired() { + return authRequired; + } + + public void setAuthRequired(Boolean authRequired) { + this.authRequired = authRequired; + } + + public Boolean getPaymentRequired() { + return paymentRequired; + } + + public void setPaymentRequired(Boolean paymentRequired) { + this.paymentRequired = paymentRequired; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoPayment.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoPayment.java new file mode 100644 index 0000000..cd8f10a --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoPayment.java @@ -0,0 +1,78 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplChuch; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public final class NoStrRelayInfoPayment implements NoStrGospel { + + private final List kinds = new ArrayList<>(); + private Integer amount; + private String unit; + + public NoStrRelayInfoPayment(int amount, String unit) { + this.amount = amount; + this.unit = unit; + } + + public NoStrRelayInfoPayment(JsonObject json) { + NoStrKindValue.readArrayKinds(json, QName.KINDS, kinds); + amount = NoStrImplChuch.readInteger(json, QName.AMOUNT); + unit = NoStrImplChuch.readString(json, QName.UNIT); + } + + @Override + public JsonValue toBible() { + JsonObjectBuilder result = Json.createObjectBuilder(); + NoStrKindValue.writeArrayKinds(result, QName.KINDS, kinds); + NoStrImplChuch.writeInteger(result, QName.AMOUNT, amount); + NoStrImplChuch.writeString(result, QName.UNIT, unit); + return result.build(); + } + + private enum QName implements NoStrImplQName { + + KINDS, + AMOUNT, + UNIT, + ; + + private final String jsonQName; + + private QName() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + } + + public List getKinds() { + return kinds; + } + + public Integer getAmount() { + return amount; + } + + public void setAmount(Integer amount) { + this.amount = amount; + } + + public String getUnit() { + return unit; + } + + public void setUnit(String unit) { + this.unit = unit; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoRetention.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoRetention.java new file mode 100644 index 0000000..ce9a16a --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoRetention.java @@ -0,0 +1,81 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.ArrayList; +import java.util.List; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplChuch; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public final class NoStrRelayInfoRetention implements NoStrGospel { + + private final List kinds = new ArrayList<>(); + private Integer time; + private Integer count; + + public NoStrRelayInfoRetention() { + } + + public NoStrRelayInfoRetention(Integer time, Integer count) { + this.time = time; + this.count = count; + } + + public NoStrRelayInfoRetention(JsonObject json) { + NoStrKindValue.readArrayKinds(json, QName.KINDS, kinds); + time = NoStrImplChuch.readInteger(json, QName.TIME); + count = NoStrImplChuch.readInteger(json, QName.COUNT); + } + + @Override + public JsonValue toBible() { + JsonObjectBuilder result = Json.createObjectBuilder(); + NoStrKindValue.writeArrayKinds(result, QName.KINDS, kinds); + NoStrImplChuch.writeInteger(result, QName.TIME, time); + NoStrImplChuch.writeInteger(result, QName.COUNT, count); + return result.build(); + } + + private enum QName implements NoStrImplQName { + + KINDS, + TIME, + COUNT, + ; + + private final String jsonQName; + + private QName() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + } + + public List getKinds() { + return kinds; + } + + public Integer getTime() { + return time; + } + + public void setTime(Integer time) { + this.time = time; + } + + public Integer getCount() { + return count; + } + + public void setCount(Integer count) { + this.count = count; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrSubscriptionFilter.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrSubscriptionFilter.java new file mode 100644 index 0000000..99353e4 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrSubscriptionFilter.java @@ -0,0 +1,133 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagMeta; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplChuch; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public class NoStrSubscriptionFilter implements NoStrGospel { + + private final static String TAG_FILTER_PREFIX = "#"; + private final List ids = new ArrayList<>(); + private final List authors = new ArrayList<>(); + private final List kinds = new ArrayList<>(); + private final Map tags = new HashMap<>(); + private Integer since; + private Integer until; + private Integer limit; + + private enum QName implements NoStrImplQName { + IDS, AUTHORS, KINDS, SINCE, UNTIL, LIMIT; + + private final String jsonQName; + + private QName() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + } + + public NoStrSubscriptionFilter() { + } + + public NoStrSubscriptionFilter(JsonObject json) { + NoStrImplChuch.readArrayString(json, QName.IDS, ids); + NoStrImplChuch.readArrayString(json, QName.AUTHORS, authors); + NoStrKindValue.readArrayKinds(json, QName.KINDS, kinds); + for (String jsonName : json.keySet()) { + if (!jsonName.startsWith(TAG_FILTER_PREFIX)) { + continue; + } + String jsonNameKey = jsonName.substring(0, 1); + if (tags.containsKey(jsonNameKey)) { + continue; + } + JsonValue arrayJson = json.get(jsonNameKey); + if (!JsonValue.ValueType.ARRAY.equals(arrayJson.getValueType())) { + continue; + } + Stream tagFilterValues = arrayJson.asJsonArray().stream(); + List tagFilters = tagFilterValues.map(v -> ((JsonString)v).getString()).collect(Collectors.toList()); + NoStrEventTagMeta eventTag = NoStrEventTagMeta.valueOf(jsonNameKey); + tags.put(jsonNameKey, new NoStrSubscriptionFilterTag(eventTag, tagFilters)); + } + since = NoStrImplChuch.readInteger(json, QName.SINCE); + until = NoStrImplChuch.readInteger(json, QName.UNTIL); + limit = NoStrImplChuch.readInteger(json, QName.LIMIT); + } + + @Override + public JsonValue toBible() { + JsonObjectBuilder result = Json.createObjectBuilder(); + NoStrImplChuch.writeArrayString(result, QName.IDS, ids); + NoStrImplChuch.writeArrayString(result, QName.AUTHORS, authors); + NoStrKindValue.writeArrayKinds(result, QName.KINDS, kinds); + for (String tagKey : tags.keySet()) { + if (tagKey.length() != 1) { + continue; + } + NoStrSubscriptionFilterTag tag = tags.get(tagKey); + result.add(TAG_FILTER_PREFIX + tagKey, Json.createArrayBuilder(tag.getKeywords())); + } + NoStrImplChuch.writeInteger(result, QName.SINCE, since); + NoStrImplChuch.writeInteger(result, QName.UNTIL, until); + NoStrImplChuch.writeInteger(result, QName.LIMIT, limit); + return result.build(); + } + + public Integer getSince() { + return since; + } + + public void setSince(Integer since) { + this.since = since; + } + + public Integer getUntil() { + return until; + } + + public void setUntil(Integer until) { + this.until = until; + } + + public Integer getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + this.limit = limit; + } + + public List getIds() { + return ids; + } + + public List getAuthors() { + return authors; + } + + public List getKinds() { + return kinds; + } + + public Map getTags() { + return tags; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrSubscriptionFilterTag.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrSubscriptionFilterTag.java new file mode 100644 index 0000000..ce482e9 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/NoStrSubscriptionFilterTag.java @@ -0,0 +1,25 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.util.ArrayList; +import java.util.List; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagMeta; + +public final class NoStrSubscriptionFilterTag { + + private final NoStrEventTagMeta eventTag; + private final List keywords = new ArrayList<>(); + + public NoStrSubscriptionFilterTag(NoStrEventTagMeta eventTag, List keywords) { + this.eventTag = eventTag; + this.keywords.addAll(keywords); + } + + public NoStrEventTagMeta getEventTag() { + return eventTag; + } + + public List getKeywords() { + return keywords; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEvent.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEvent.java new file mode 100644 index 0000000..d5371fe --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEvent.java @@ -0,0 +1,100 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonObject; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public final class NoStrEvent implements NoStrGospel { + + private final NoStrEventId id; + private final NoStrEventPayload payload; + private final NoStrEventSignature signature; + + private enum QName implements NoStrImplQName { + + ID, PUBKEY, CREATED_AT, KIND, TAGS, CONTENT, SIG; + + private final String jsonQName; + + private QName() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + } + + public NoStrEvent(NoStrEventId id, NoStrEventPayload payload, NoStrEventSignature signature) { + this.id = Objects.requireNonNull(id); + this.payload = Objects.requireNonNull(payload); + this.signature = Objects.requireNonNull(signature); + } + + public NoStrEvent(JsonObject event) { + List eventTags = new ArrayList<>(); + JsonArray jsonTags = event.getJsonArray(QName.TAGS.getQName()); + for (JsonValue jsonTag : jsonTags) { + Iterator i = jsonTag.asJsonArray().iterator(); + if (!i.hasNext()) { + throw new IllegalArgumentException("Empty tag"); + } + String tagStr = ((JsonString)i.next()).getString(); + NoStrEventTagMeta tagMeta = NoStrEventTagMeta.valueOf(tagStr); + List args = new ArrayList<>(); + while (i.hasNext()) { + args.add(((JsonString)i.next()).getString()); + } + eventTags.add(NoStrEventTag.valueOf(tagMeta, args.toArray(new String[] {}))); + } + long createdAtLong = event.getJsonNumber(QName.CREATED_AT.getQName()).longValue(); + int kindNumber = event.getInt(QName.KIND.getQName()); + String idStr = event.getString(QName.ID.getQName()); + String signatureStr = event.getString(QName.SIG.getQName()); + String pubkeyStr = event.getString(QName.PUBKEY.getQName()); + String content = event.getString(QName.CONTENT.getQName()); + NoStrIdentityPublicKey pubkey = NoStrIdentityPublicKey.ofHex(pubkeyStr); + Instant createdAt = Instant.ofEpochSecond(createdAtLong); + NoStrEventKind kind = NoStrEventKind.valueOf(kindNumber); + this.id = NoStrEventId.ofHex(idStr); + this.payload = new NoStrEventPayload(pubkey, createdAt, kind, eventTags, content); + this.signature = NoStrEventSignature.ofHex(signatureStr); + } + + @Override + public JsonValue toBible() { + return Json.createObjectBuilder() + .add(QName.ID.getQName(), id.getHex()) + .add(QName.PUBKEY.getQName(), payload.getPublicKey().getHex()) + .add(QName.CREATED_AT.getQName(), payload.getCreatedAt().getEpochSecond()) + .add(QName.KIND.getQName(), payload.getKind().getNumber()) + .add(QName.TAGS.getQName(), NoStrEventTag.toJsonArray(payload.getTags())) + .add(QName.CONTENT.getQName(), payload.getContent()) + .add(QName.SIG.getQName(), signature.getHex()) + .build(); + } + + public NoStrEventId getId() { + return id; + } + + public NoStrEventPayload getPayload() { + return payload; + } + + public NoStrEventSignature getSignature() { + return signature; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventId.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventId.java new file mode 100644 index 0000000..922bde9 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventId.java @@ -0,0 +1,15 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplBinHexAdapter; +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public final class NoStrEventId extends NoStrImplBinHexAdapter { + + public NoStrEventId(byte[] valueBytes) { + super(valueBytes); + } + + public static NoStrEventId ofHex(String valueHex) { + return new NoStrEventId(OctoBitFormat.HEX.toBytes(valueHex)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKind.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKind.java new file mode 100644 index 0000000..e7bd9a5 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKind.java @@ -0,0 +1,22 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventKind; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplPossibility; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQFlag; + +public interface NoStrEventKind extends NoStrImplQFlag { + + int getNumber(); + + NoStrImplPossibility getNip(); + + NoStrEventKindRange getRange(); + + static NoStrEventKind valueOf(int number) { + if (NoStrImplEventKind.valueOfKindValid(number)) { + return NoStrImplEventKind.valueOfKind(number); + } else { + return new NoStrEventKindCustom(number); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKindCustom.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKindCustom.java new file mode 100644 index 0000000..ffee227 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKindCustom.java @@ -0,0 +1,37 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImpl; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventKindRange; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplPossibility; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQFlag; + +public final class NoStrEventKindCustom implements NoStrEventKind { + + private final int number; + private final NoStrEventKindRange range; + + public NoStrEventKindCustom(int number) { + this.number = number; + this.range = NoStrImplEventKindRange.valueOfKind(number); + } + + @Override + public int getNumber() { + return number; + } + + @Override + public String getQFlag() { + return NoStrImplQFlag.CUSTOM_INDICATOR; + } + + @Override + public NoStrImplPossibility getNip() { + return NoStrImpl.NIP_0_UNKNOWN; + } + + @Override + public NoStrEventKindRange getRange() { + return range; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKindRange.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKindRange.java new file mode 100644 index 0000000..ae4a68b --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventKindRange.java @@ -0,0 +1,12 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQFlag; + +public interface NoStrEventKindRange extends NoStrImplQFlag { + + int getNumberStart(); + + int getNumberstop(); + + boolean isReplaceable(int number); +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventPayload.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventPayload.java new file mode 100644 index 0000000..4fd0b7b --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventPayload.java @@ -0,0 +1,80 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; + +public final class NoStrEventPayload { + + private final NoStrIdentityPublicKey publicKey; + private Instant createdAt; + private final NoStrEventKind kind; + private final List tags; + private final String content; + + public NoStrEventPayload(NoStrIdentityPublicKey publicKey, Instant createdAt, NoStrEventKind kind, List tags, + String content) { + this.publicKey = Objects.requireNonNull(publicKey); + this.createdAt = Objects.requireNonNull(createdAt); + this.kind = Objects.requireNonNull(kind); + this.tags = Objects.requireNonNull(tags); + this.content = Objects.requireNonNull(content); + } + + public NoStrIdentityPublicKey getPublicKey() { + return publicKey; + } + + public Instant getCreatedAt() { + return createdAt; + } + + public NoStrEventKind getKind() { + return kind; + } + + public List getTags() { + return tags; + } + + public String getContent() { + return content; + } + + public void setCreatedAt(Instant createdAt) { + this.createdAt = Objects.requireNonNull(createdAt); + } + + public List findByQName(String tagQName) { + List result = new ArrayList<>(); + for (int i=0; i < tags.size(); i++) { + NoStrEventTag tag = tags.get(i); + if (tag.getMeta().getQName().equals(tagQName)) { + result.add(tag); + } + } + return result; + } + + public List findByQName(NoStrEventTagMeta tagQName) { + return findByQName(tagQName.getQName()); + } + + public Optional findFirstByQName(String tagQName) { + for (int i=0; i < tags.size(); i++) { + NoStrEventTag tag = tags.get(i); + if (tag.getMeta().getQName().equals(tagQName)) { + return Optional.of(tag); + } + } + return Optional.empty(); + } + + public Optional findFirstByQName(NoStrEventTagMeta tagQName) { + return findFirstByQName(tagQName.getQName()); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventSignature.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventSignature.java new file mode 100644 index 0000000..6eb2a1b --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventSignature.java @@ -0,0 +1,68 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.io.StringWriter; +import java.nio.charset.StandardCharsets; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonWriter; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPrivateKey; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplBinHexAdapter; +import love.distributedrebirth.no2all.octo.OctoBitFormat; +import love.distributedrebirth.no2all.octo.trust.OctoTrust; +import love.distributedrebirth.no2all.octo.trust.OctoTrustHash; +import love.distributedrebirth.no2all.octo.trust.OctoTrustSchnorr; + +public final class NoStrEventSignature extends NoStrImplBinHexAdapter { + + public NoStrEventSignature(byte[] valueBytes) { + super(valueBytes); + } + + public static NoStrEventSignature ofHex(String valueHex) { + return new NoStrEventSignature(OctoBitFormat.HEX.toBytes(valueHex)); + } + + public static NoStrEvent sign(NoStrEventPayload payload, NoStrIdentityPrivateKey privateKey, byte[] eventId) { + byte[] signRandom = OctoTrust.createRandomByteArray(OctoTrust.KEY_LENGTH); + byte[] signHash = OctoTrustSchnorr.sign(eventId, privateKey.getBytes(), signRandom); + NoStrEventSignature signature = new NoStrEventSignature(signHash); + return new NoStrEvent(new NoStrEventId(eventId), payload, signature); + } + + public static NoStrEvent sign(NoStrEventPayload payload, NoStrIdentityPrivateKey privateKey) { + return sign(payload, privateKey, generateEventId(payload)); + } + + public static boolean verify(NoStrEvent event) { + byte[] eventId = generateEventId(event.getPayload()); + byte[] pubkey = event.getPayload().getPublicKey().getBytes(); + byte[] sig = event.getSignature().getBytes(); + boolean result = OctoTrustSchnorr.verify(eventId, pubkey, sig); + return result; + } + + public static String generateEventIdJsonString(NoStrEventPayload payload) { + JsonArray eventAsJson = Json.createArrayBuilder() + .add(0) + .add(payload.getPublicKey().getHex()) + .add(payload.getCreatedAt().getEpochSecond()) + .add(payload.getKind().getNumber()) + .add(NoStrEventTag.toJsonArray(payload.getTags())) + .add(payload.getContent()) + .build(); + StringWriter eventAsString = new StringWriter(); + try (JsonWriter writer = Json.createWriter(eventAsString)) { + writer.write(eventAsJson); + } + return eventAsString.getBuffer().toString(); + } + + public static byte[] generateEventIdHash(String eventIdJsonString) { + return OctoTrustHash.sha256(eventIdJsonString.getBytes(StandardCharsets.UTF_8)); + } + + public static byte[] generateEventId(NoStrEventPayload payload) { + return generateEventIdHash(generateEventIdJsonString(payload)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTag.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTag.java new file mode 100644 index 0000000..d6fee12 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTag.java @@ -0,0 +1,83 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagE; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagNonce; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public interface NoStrEventTag extends NoStrGospel { + + NoStrEventTagMeta getMeta(); + + String[] getMetaArguments(); + + default Optional getMetaArgument(int idx) { + if (idx >= getMetaArguments().length || idx < 0) { + return Optional.empty(); + } + return Optional.ofNullable(getMetaArguments()[idx]); + } + + default Optional getMetaArgument(NoStrImplEventTagArgument idx) { + return getMetaArgument(idx.ordinal()); + } + + default List getMetaSafeArguments() { + List result = new ArrayList<>(); + int idxLastValue = 0; + String[] values = getMetaArguments(); + for (int i = 0; i < values.length; i++) { + String str = values[i]; + if (str == null) { + result.add(""); // optional + } else { + result.add(str); + idxLastValue = i; + } + } + if (idxLastValue < result.size()) { + result = result.subList(0, idxLastValue + 1); + } + return result; + } + + @Override + default JsonValue toBible() { + return Json.createArrayBuilder() + .add(getMeta().getQName()) + .addAll(Json.createArrayBuilder(getMetaSafeArguments())) + .build(); + } + + static JsonArray toJsonArray(List tags) { + return Json.createArrayBuilder( + tags.stream().map(v -> v.toBible()).collect(Collectors.toList()) + ).build(); + } + + static NoStrEventTag valueOfArgs(NoStrEventTagMeta meta, String... args) { + return valueOf(meta, args); + } + + static NoStrEventTag valueOf(NoStrEventTagMeta meta, String[] args) { + Objects.requireNonNull(meta); + Objects.requireNonNull(args); + if (NoStrImplEventTag.E.equals(meta)) { + return new NoStrTagE(args); + } + if (NoStrImplEventTag.NONCE.equals(meta)) { + return new NoStrTagNonce(args); + } + return new NoStrEventTagCustom(meta, args); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagBase.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagBase.java new file mode 100644 index 0000000..96affb9 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagBase.java @@ -0,0 +1,65 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.util.Objects; +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public class NoStrEventTagBase implements NoStrEventTag { + + private final NoStrEventTagMeta meta; + private String[] args; + + public NoStrEventTagBase(NoStrEventTagMeta meta) { + this(meta, new String[4]); // limit autoGrow to zero in 99% of tags + } + + public NoStrEventTagBase(NoStrEventTagMeta meta, String[] args) { + this.meta = Objects.requireNonNull(meta); + this.args = Objects.requireNonNull(args); + } + + @Override + public NoStrEventTagMeta getMeta() { + return meta; + } + + @Override + public String[] getMetaArguments() { + return args; + } + + private int autoGrow(int index) { + if (index < args.length) { + return index; + } + String[] result = new String[index]; + System.arraycopy(args, 0, result, 0, args.length); + args = result; + return index; + } + +// public Optional getMetaArgument(T arg) { +// return NoStrEventTag.super.getMetaArgument(arg.ordinal()); +// } + + protected void setMetaArgumentStr(T arg, String value) { + int index = autoGrow(arg.ordinal()); + args[index] = value; + } + + protected void setMetaArgumentOptStr(T arg, Optional value) { + if (value.isEmpty()) { + return; + } + setMetaArgumentStr(arg, value.get()); + } + + protected void setMetaArgumentOptQName(T arg, Optional value) { + if (value.isEmpty()) { + return; + } + setMetaArgumentStr(arg, value.get().getQName()); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagBaseMono.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagBaseMono.java new file mode 100644 index 0000000..1fae047 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagBaseMono.java @@ -0,0 +1,53 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.util.Objects; +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public class NoStrEventTagBaseMono implements NoStrEventTag { + + private final NoStrEventTagMeta meta; + private String[] args; + + public NoStrEventTagBaseMono(NoStrEventTagMeta meta) { + this(meta, new String[1]); + } + + public NoStrEventTagBaseMono(NoStrEventTagMeta meta, String[] args) { + this.meta = Objects.requireNonNull(meta); + this.args = Objects.requireNonNull(args); + } + + @Override + public NoStrEventTagMeta getMeta() { + return meta; + } + + @Override + public String[] getMetaArguments() { + return args; + } + + public Optional getMetaArgument() { + return NoStrEventTag.super.getMetaArgument(0); + } + + protected void setMetaArgumentStr(String value) { + args[0] = value; + } + + protected void setMetaArgumentOptStr(Optional value) { + if (value.isEmpty()) { + return; + } + setMetaArgumentStr(value.get()); + } + + protected void setMetaArgumentOptQName(Optional value) { + if (value.isEmpty()) { + return; + } + setMetaArgumentStr(value.get().getQName()); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagCustom.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagCustom.java new file mode 100644 index 0000000..f0f7f13 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagCustom.java @@ -0,0 +1,24 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.util.Objects; + +public final class NoStrEventTagCustom implements NoStrEventTag { + + private final NoStrEventTagMeta meta; + private final String[] args; + + public NoStrEventTagCustom(NoStrEventTagMeta meta, String[] args) { + this.meta = Objects.requireNonNull(meta); + this.args = Objects.requireNonNull(args); + } + + @Override + public NoStrEventTagMeta getMeta() { + return meta; + } + + @Override + public String[] getMetaArguments() { + return args; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagMeta.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagMeta.java new file mode 100644 index 0000000..75058a5 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagMeta.java @@ -0,0 +1,23 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplPossibility; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQFlag; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public interface NoStrEventTagMeta extends NoStrImplQName, NoStrImplQFlag { + + boolean isQuariable(); + + boolean isSinglton(); + + NoStrImplPossibility[] getNips(); + + static NoStrEventTagMeta valueOf(String qName) { + if (NoStrImplEventTag.valueOfWireValid(qName)) { + return NoStrImplEventTag.valueOfWire(qName); + } else { + return new NoStrEventTagMetaCustom(qName); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagMetaCustom.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagMetaCustom.java new file mode 100644 index 0000000..f94eb2a --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagMetaCustom.java @@ -0,0 +1,45 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.util.Objects; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImpl; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplPossibility; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQFlag; + +public class NoStrEventTagMetaCustom implements NoStrEventTagMeta { + + private static final NoStrImplPossibility[] UNKNOWN_NIPS = new NoStrImplPossibility[] { NoStrImpl.NIP_0_UNKNOWN }; + private final String qName; + private final boolean quariable; + + public NoStrEventTagMetaCustom(String qName) { + this.qName = Objects.requireNonNull(qName); + this.quariable = NoStrImplEventTag.isQuariable(qName); + } + + @Override + public String getQName() { + return qName; + } + + @Override + public String getQFlag() { + return NoStrImplQFlag.CUSTOM_INDICATOR; + } + + @Override + public NoStrImplPossibility[] getNips() { + return UNKNOWN_NIPS; + } + + @Override + public boolean isQuariable() { + return quariable; + } + + @Override + public boolean isSinglton() { + return false; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClient.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClient.java new file mode 100644 index 0000000..7060b4c --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClient.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import love.distributedrebirth.no2all.nostr.NoStrGospel; + +public interface NoStrMsgToClient extends NoStrGospel { + // marker +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientAuth.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientAuth.java new file mode 100644 index 0000000..da1bccd --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientAuth.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToClientAuth implements NoStrMsgToClient { + + private final String challenge; + + public NoStrMsgToClientAuth(String challenge) { + this.challenge = Objects.requireNonNull(challenge); + } + + public NoStrMsgToClientAuth(JsonArray json) { + this.challenge = ((JsonString)json.get(1)).getString(); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.AUTH.getQName()) + .add(challenge) + .build(); + } + + public String getChallenge() { + return challenge; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientCount.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientCount.java new file mode 100644 index 0000000..a4ef1ca --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientCount.java @@ -0,0 +1,43 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToClientCount implements NoStrMsgToClient { + + private final String QNAME_COUNT = "count"; + private final String subscriptionId; + private final int count; + + public NoStrMsgToClientCount(String subscriptionId, int count) { + this.subscriptionId = Objects.requireNonNull(subscriptionId); + this.count = count; + } + + public NoStrMsgToClientCount(JsonArray json) { + this.subscriptionId = ((JsonString)json.get(1)).getString(); + this.count = json.get(2).asJsonObject().getInt(QNAME_COUNT); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.COUNT.getQName()) + .add(subscriptionId) + .add(Json.createObjectBuilder().add(QNAME_COUNT, count)) + .build(); + } + + public String getSubscriptionId() { + return subscriptionId; + } + + public int getCount() { + return count; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientEose.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientEose.java new file mode 100644 index 0000000..1aa80d0 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientEose.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToClientEose implements NoStrMsgToClient { + + private final String subscriptionId; + + public NoStrMsgToClientEose(String subscriptionId) { + this.subscriptionId = Objects.requireNonNull(subscriptionId); + } + + public NoStrMsgToClientEose(JsonArray json) { + this.subscriptionId = ((JsonString)json.get(1)).getString(); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.EOSE.getQName()) + .add(subscriptionId) + .build(); + } + + public String getSubscriptionId() { + return subscriptionId; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientEvent.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientEvent.java new file mode 100644 index 0000000..571f94c --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientEvent.java @@ -0,0 +1,43 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToClientEvent implements NoStrMsgToClient { + + private final String subscriptionId; + private final NoStrEvent event; + + public NoStrMsgToClientEvent(String subscriptionId, NoStrEvent event) { + this.subscriptionId = Objects.requireNonNull(subscriptionId); + this.event = Objects.requireNonNull(event); + } + + public NoStrMsgToClientEvent(JsonArray json) { + this.subscriptionId = ((JsonString)json.get(1)).getString(); + this.event = new NoStrEvent(json.get(2).asJsonObject()); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.EVENT.getQName()) + .add(subscriptionId) + .add(event.toBible()) + .build(); + } + + public String getSubscriptionId() { + return subscriptionId; + } + + public NoStrEvent getEvent() { + return event; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientNotice.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientNotice.java new file mode 100644 index 0000000..a23dc5e --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientNotice.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToClientNotice implements NoStrMsgToClient { + + private final String message; + + public NoStrMsgToClientNotice(String message) { + this.message = Objects.requireNonNull(message); + } + + public NoStrMsgToClientNotice(JsonArray json) { + this.message = ((JsonString)json.get(1)).getString(); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.NOTICE.getQName()) + .add(message) + .build(); + } + + public String getMessage() { + return message; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientOk.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientOk.java new file mode 100644 index 0000000..1dfe7d2 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToClientOk.java @@ -0,0 +1,66 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventId; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageOkReason; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToClientOk implements NoStrMsgToClient { + + private final NoStrEventId eventId; + private final boolean saved; + private final String message; + private final NoStrImplMessageOkReason reason; + + public NoStrMsgToClientOk(NoStrEventId eventId, boolean saved, NoStrImplMessageOkReason reason, String message) { + this.eventId = Objects.requireNonNull(eventId); + this.saved = saved; + this.reason = Objects.requireNonNull(reason); + this.message = reason.getPrefix() + " " + message; + } + + public NoStrMsgToClientOk(NoStrEventId eventId, boolean saved, String message) { + this.eventId = Objects.requireNonNull(eventId); + this.saved = saved; + this.message = Objects.requireNonNull(message); + this.reason = NoStrImplMessageOkReason.valueOfPrefix(message); + } + + public NoStrMsgToClientOk(JsonArray json) { + this.eventId = NoStrEventId.ofHex(((JsonString)json.get(1)).getString()); + this.saved = JsonValue.TRUE.equals(json.get(2)); + this.message = ((JsonString)json.get(3)).getString(); + this.reason = NoStrImplMessageOkReason.valueOfPrefix(message); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.OK.getQName()) + .add(eventId.getHex()) + .add(saved) + .add(message) + .build(); + } + + public NoStrEventId getEventId() { + return eventId; + } + + public boolean isSaved() { + return saved; + } + + public String getMessage() { + return message; + } + + public NoStrImplMessageOkReason getReason() { + return reason; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelay.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelay.java new file mode 100644 index 0000000..3ee8f39 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelay.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import love.distributedrebirth.no2all.nostr.NoStrGospel; + +public interface NoStrMsgToRelay extends NoStrGospel { + // marker +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayAuth.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayAuth.java new file mode 100644 index 0000000..dfdd981 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayAuth.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToRelayAuth implements NoStrMsgToRelay { + + private final NoStrEvent event; + + public NoStrMsgToRelayAuth(NoStrEvent event) { + this.event = Objects.requireNonNull(event); + } + + public NoStrMsgToRelayAuth(JsonArray json) { + this.event = new NoStrEvent(json.get(1).asJsonObject()); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.AUTH.getQName()) + .add(event.toBible()) + .build(); + } + + public NoStrEvent getEvent() { + return event; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayClose.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayClose.java new file mode 100644 index 0000000..dc6d1e7 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayClose.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToRelayClose implements NoStrMsgToRelay { + + private final String subscriptionId; + + public NoStrMsgToRelayClose(String subscriptionId) { + this.subscriptionId = Objects.requireNonNull(subscriptionId); + } + + public NoStrMsgToRelayClose(JsonArray json) { + this.subscriptionId = ((JsonString)json.get(1)).getString(); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.CLOSE.getQName()) + .add(subscriptionId) + .build(); + } + + public String getSubscriptionId() { + return subscriptionId; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayCount.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayCount.java new file mode 100644 index 0000000..5ffba4a --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayCount.java @@ -0,0 +1,49 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.model.NoStrSubscriptionFilter; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToRelayCount implements NoStrMsgToRelay { + + private final String subscriptionId; + private final List filters = new ArrayList<>(); + + public NoStrMsgToRelayCount(String subscriptionId, NoStrSubscriptionFilter... filters) { + this.subscriptionId = Objects.requireNonNull(subscriptionId); + this.filters.addAll(Arrays.asList(filters)); + } + + public NoStrMsgToRelayCount(JsonArray json) { + this.subscriptionId = ((JsonString)json.get(1)).getString(); + for (int i = 2; i < json.size(); i++) { + filters.add(new NoStrSubscriptionFilter(json.get(i).asJsonObject())); + } + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.COUNT.getQName()) + .add(subscriptionId) + .addAll(Json.createArrayBuilder(filters.stream().map(v -> v.toBible()).collect(Collectors.toList()))) + .build(); + } + + public String getSubscriptionId() { + return subscriptionId; + } + + public List getFilters() { + return filters; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayEvent.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayEvent.java new file mode 100644 index 0000000..fd5f026 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayEvent.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.Objects; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToRelayEvent implements NoStrMsgToRelay { + + private final NoStrEvent event; + + public NoStrMsgToRelayEvent(NoStrEvent event) { + this.event = Objects.requireNonNull(event); + } + + public NoStrMsgToRelayEvent(JsonArray json) { + this.event = new NoStrEvent(json.get(1).asJsonObject()); + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.EVENT.getQName()) + .add(event.toBible()) + .build(); + } + + public NoStrEvent getEvent() { + return event; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayReq.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayReq.java new file mode 100644 index 0000000..a1d856d --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/message/NoStrMsgToRelayReq.java @@ -0,0 +1,49 @@ +package love.distributedrebirth.no2all.nostr.model.message; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.model.NoStrSubscriptionFilter; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageType; + +public final class NoStrMsgToRelayReq implements NoStrMsgToRelay { + + private final String subscriptionId; + private final List filters = new ArrayList<>(); + + public NoStrMsgToRelayReq(String subscriptionId, NoStrSubscriptionFilter... filters) { + this.subscriptionId = Objects.requireNonNull(subscriptionId); + this.filters.addAll(Arrays.asList(filters)); + } + + public NoStrMsgToRelayReq(JsonArray json) { + this.subscriptionId = ((JsonString)json.get(1)).getString(); + for (int i = 2; i < json.size(); i++) { + filters.add(new NoStrSubscriptionFilter(json.get(i).asJsonObject())); + } + } + + @Override + public JsonValue toBible() { + return Json.createArrayBuilder() + .add(NoStrImplMessageType.REQ.getQName()) + .add(subscriptionId) + .addAll(Json.createArrayBuilder(filters.stream().map(v -> v.toBible()).collect(Collectors.toList()))) + .build(); + } + + public String getSubscriptionId() { + return subscriptionId; + } + + public List getFilters() { + return filters; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareB32LinkBare.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareB32LinkBare.java new file mode 100644 index 0000000..c4e3e49 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareB32LinkBare.java @@ -0,0 +1,32 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.util.Objects; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public abstract class NoStrShareB32LinkBare implements NoStrShareLink { + + private final String shareLink; + private final byte[] bareValue; + + public NoStrShareB32LinkBare(NoStrImplShareLink linkType, OctoBech32String link) { + this(linkType, OctoBech32.fromBech32(linkType.validate(link)).getData()); + } + + public NoStrShareB32LinkBare(NoStrImplShareLink linkType, byte[] bareValue) { + Objects.requireNonNull(bareValue); + this.shareLink = OctoBech32.toBech32(linkType.getPrefix(), bareValue).getValue(); + this.bareValue = bareValue; + } + + @Override + public final String toShareLink() { + return shareLink; + } + + protected byte[] getBareValue() { + return bareValue; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareB32LinkTLV.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareB32LinkTLV.java new file mode 100644 index 0000000..0c8a011 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareB32LinkTLV.java @@ -0,0 +1,49 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.util.Optional; +import java.util.function.Consumer; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareTLV; +import love.distributedrebirth.no2all.octo.OctoBech32; +import love.distributedrebirth.no2all.octo.OctoBech32Bucket; +import love.distributedrebirth.no2all.octo.OctoBech32String; +import love.distributedrebirth.no2all.octo.OctoBitChain; +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public abstract class NoStrShareB32LinkTLV implements NoStrShareLink { + + private final NoStrShareBitChainTapeDeck tapeDeck; + private final String shareLink; + + public NoStrShareB32LinkTLV(NoStrImplShareLink linkType, OctoBech32String link) { + OctoBech32Bucket shareB32 = OctoBech32.fromBech32(linkType.validate(link)); + OctoBitChain chain = new OctoBitChain(shareB32.getData()); + this.tapeDeck = new NoStrShareBitChainTapeDeck(chain); + this.shareLink = createShareLink(chain, linkType); + } + + public NoStrShareB32LinkTLV(NoStrImplShareLink linkType, Consumer tapeStudio) { + OctoBitChain chain = new OctoBitChain(); + tapeStudio.accept(new NoStrShareBitChainTapeStudio(chain)); + this.tapeDeck = new NoStrShareBitChainTapeDeck(chain); + this.shareLink = createShareLink(chain, linkType); + } + + private static String createShareLink(OctoBitChain chain, NoStrImplShareLink linkType) { + return OctoBech32.toBech32(linkType.getPrefix(), chain.toByteArray()).getValue(); + } + + protected NoStrShareBitChainTapeDeck getTapeDeck() { + return tapeDeck; + } + + public Optional getClient() { + return getTapeDeck().playBitFormatOf(NoStrImplShareTLV.CLIENT, OctoBitFormat.UTF8); + } + + @Override + public final String toShareLink() { + return shareLink; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareBitChainTapeDeck.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareBitChainTapeDeck.java new file mode 100644 index 0000000..da0bf44 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareBitChainTapeDeck.java @@ -0,0 +1,94 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKind; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareTLV; +import love.distributedrebirth.no2all.octo.OctoBitChain; +import love.distributedrebirth.no2all.octo.OctoBitChainFrame; +import love.distributedrebirth.no2all.octo.OctoBitConverter; +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class NoStrShareBitChainTapeDeck { + + protected final OctoBitChain chain; + + public NoStrShareBitChainTapeDeck(OctoBitChain chain) { + this.chain = Objects.requireNonNull(chain); + } + + public int getFrameCount() { + return chain.getFrameCount(); + } + + public Optional playByteBufferOf(NoStrImplShareTLV tlv) { + byte[] data = chain.getFirstDataOfType(tlv); + if (data == null) { + return Optional.empty(); + } + if (data.length == 0) { + return Optional.empty(); + } + return Optional.of(ByteBuffer.wrap(data)); + } + + public Optional playSpecial() { + return playByteBufferOf(NoStrImplShareTLV.SPECIAL); + } + + public Optional playSpecial(OctoBitConverter type) { + Optional data = playSpecial(); + if (data.isEmpty()) { + return Optional.empty(); + } + return Optional.of(type.fromBytes(data.get().array())); + } + + public List playRelays() { + List result = new ArrayList<>(); + for (OctoBitChainFrame frame : chain.getFramesByType(NoStrImplShareTLV.RELAY)) { + result.add(OctoBitFormat.IDN.fromBytes(frame.getData())); + } + return result; + } + + public Optional playAuthor() { + byte[] data = chain.getFirstDataOfType(NoStrImplShareTLV.AUTHOR); + if (data == null) { + return Optional.empty(); + } + return Optional.of(new NoStrIdentityPublicKey(data)); + } + + public Optional playKind() { + byte[] data = chain.getFirstDataOfType(NoStrImplShareTLV.KIND); + if (data == null) { + return Optional.empty(); + } + return Optional.of(NoStrEventKind.valueOf(OctoBitFormat.BI_UI32.fromBytes(data).intValue())); + } + + public Optional playByteOf(NoStrImplShareTLV tlv) { + byte[] data = chain.getFirstDataOfType(tlv); + if (data == null) { + return Optional.empty(); + } + if (data.length == 0) { + return Optional.empty(); + } + return Optional.of(data[0]); + } + + public Optional playBitFormatOf(NoStrImplShareTLV tlv, OctoBitConverter type) { + byte[] data = chain.getFirstDataOfType(tlv); + if (data == null) { + return Optional.empty(); + } + return Optional.of(type.fromBytes(data)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareBitChainTapeStudio.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareBitChainTapeStudio.java new file mode 100644 index 0000000..4ccbc21 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareBitChainTapeStudio.java @@ -0,0 +1,85 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKind; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareTLV; +import love.distributedrebirth.no2all.octo.OctoBitChain; +import love.distributedrebirth.no2all.octo.OctoBitConverter; +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class NoStrShareBitChainTapeStudio extends NoStrShareBitChainTapeDeck { + + public NoStrShareBitChainTapeStudio(OctoBitChain chain) { + super(chain); + } + + public void clear() { + chain.clear(); + } + + public void recordClient(String client) { + if (client == null) { + return; + } + chain.addFrame(NoStrImplShareTLV.CLIENT, OctoBitFormat.UTF8.toBytes(client)); + } + + public void recordByteArrayFor(byte[] value, NoStrImplShareTLV tlv) { + if (value == null) { + return; + } + chain.addFrame(tlv, value); + } + + public void recordSpecial(byte[] value) { + recordByteArrayFor(Objects.requireNonNull(value), NoStrImplShareTLV.SPECIAL); + } + + public void recordSpecial(T value, OctoBitConverter type) { + recordSpecial(type.toBytes(Objects.requireNonNull(value))); + } + + public void recordRelays(List relays) { + if (relays == null) { + return; + } + for (String relay : relays) { + chain.addFrame(NoStrImplShareTLV.RELAY, OctoBitFormat.IDN.toBytes(relay)); + } + } + + public void recordAuthor(Optional value) { + if (value.isEmpty()) { + return; + } + recordAuthor(value.get()); + } + + public void recordAuthor(NoStrIdentityPublicKey value) { + recordByteArrayFor(value.getBytes(), NoStrImplShareTLV.AUTHOR); + } + + public void recordKind(Optional value) { + if (value.isEmpty()) { + return; + } + recordKind(value.get()); + } + + public void recordKind(NoStrEventKind value) { + if (value == null) { + return; + } + chain.addFrame(NoStrImplShareTLV.KIND, OctoBitFormat.BI_UI32.toBytes((long)value.getNumber())); + } + + public void recordByteFor(byte value, NoStrImplShareTLV tlv) { + byte[] data = new byte[1]; + data[0] = value; + chain.addFrame(tlv, data); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLink.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLink.java new file mode 100644 index 0000000..16711d2 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLink.java @@ -0,0 +1,6 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +public interface NoStrShareLink { + + String toShareLink(); +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEvent.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEvent.java new file mode 100644 index 0000000..844e284 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEvent.java @@ -0,0 +1,50 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.util.List; +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventId; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKind; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public final class NoStrShareLinkEvent extends NoStrShareB32LinkTLV { + + public NoStrShareLinkEvent(OctoBech32String link) { + super(NoStrImplShareLink.EVENT, link); + } + + public NoStrShareLinkEvent(NoStrEventId eventId) { + this(eventId, List.of(), Optional.empty(), Optional.empty()); + } + + public NoStrShareLinkEvent(NoStrEventId eventId, List relays, Optional author, Optional kind) { + super(NoStrImplShareLink.EVENT, tapeStudio -> { + tapeStudio.recordSpecial(eventId.getBytes()); + tapeStudio.recordRelays(relays); + tapeStudio.recordAuthor(author); + tapeStudio.recordKind(kind); + }); + } + + public NoStrEventId getEventId() { + return new NoStrEventId(getTapeDeck().playSpecial().get().array()); + } + + public List getRelays() { + return getTapeDeck().playRelays(); + } + + public Optional getAuthorId() { + return getTapeDeck().playAuthor(); + } + + public Optional getKind() { + return getTapeDeck().playKind(); + } + + public static NoStrShareLinkEvent ofB32(String linkB32) { + return new NoStrShareLinkEvent(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEventReference.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEventReference.java new file mode 100644 index 0000000..bdedaa2 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEventReference.java @@ -0,0 +1,46 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.util.List; +import java.util.Objects; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKind; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32String; +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public final class NoStrShareLinkEventReference extends NoStrShareB32LinkTLV { + + public NoStrShareLinkEventReference(OctoBech32String link) { + super(NoStrImplShareLink.EVENT_REFERENCE, link); + } + + public NoStrShareLinkEventReference(String refDValue, List relays, NoStrIdentityPublicKey author, NoStrEventKind kind) { + super(NoStrImplShareLink.EVENT_REFERENCE, tapeStudio -> { + tapeStudio.recordSpecial(refDValue, OctoBitFormat.UTF8); + tapeStudio.recordRelays(relays); + tapeStudio.recordAuthor(author); + tapeStudio.recordKind(Objects.requireNonNull(kind)); + }); + } + + public String getReferenceDValue() { + return getTapeDeck().playSpecial(OctoBitFormat.UTF8).get(); + } + + public List getRelays() { + return getTapeDeck().playRelays(); + } + + public NoStrIdentityPublicKey getAuthorId() { + return getTapeDeck().playAuthor().get(); + } + + public NoStrEventKind getKind() { + return getTapeDeck().playKind().get(); + } + + public static NoStrShareLinkEventReference ofB32(String linkB32) { + return new NoStrShareLinkEventReference(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkNote.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkNote.java new file mode 100644 index 0000000..2b3ea78 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkNote.java @@ -0,0 +1,24 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventId; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public final class NoStrShareLinkNote extends NoStrShareB32LinkBare { + + public NoStrShareLinkNote(OctoBech32String link) { + super(NoStrImplShareLink.NOTE, link); + } + + public NoStrShareLinkNote(NoStrEventId eventId) { + super(NoStrImplShareLink.NOTE, eventId.getBytes()); + } + + public NoStrEventId getEventId() { + return new NoStrEventId(getBareValue()); + } + + public static NoStrShareLinkNote ofB32(String linkB32) { + return new NoStrShareLinkNote(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKey.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKey.java new file mode 100644 index 0000000..0fc2ac9 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKey.java @@ -0,0 +1,24 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPrivateKey; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public final class NoStrShareLinkPrivateKey extends NoStrShareB32LinkBare { + + public NoStrShareLinkPrivateKey(OctoBech32String link) { + super(NoStrImplShareLink.PRIVATE_KEY, link); + } + + public NoStrShareLinkPrivateKey(NoStrIdentityPrivateKey privateKey) { + super(NoStrImplShareLink.PRIVATE_KEY, privateKey.getBytes()); + } + + public NoStrIdentityPrivateKey getPrivateKey() { + return new NoStrIdentityPrivateKey(getBareValue()); + } + + public static NoStrShareLinkPrivateKey ofB32(String linkB32) { + return new NoStrShareLinkPrivateKey(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKeySecure.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKeySecure.java new file mode 100644 index 0000000..f101d39 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKeySecure.java @@ -0,0 +1,51 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareTLV; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public final class NoStrShareLinkPrivateKeySecure extends NoStrShareB32LinkTLV { + + public NoStrShareLinkPrivateKeySecure(OctoBech32String link) { + super(NoStrImplShareLink.PRIVATE_KEY_SECURE, link); + } + + public NoStrShareLinkPrivateKeySecure(byte[] cryptKey, byte version, byte logN, byte[] salt, byte[] nonce, byte level) { + super(NoStrImplShareLink.PRIVATE_KEY_SECURE, tapeStudio -> { + tapeStudio.recordSpecial(cryptKey); + tapeStudio.recordByteFor(version, NoStrImplShareTLV.VERSION); + tapeStudio.recordByteFor(logN, NoStrImplShareTLV.LOG_N); + tapeStudio.recordByteArrayFor(salt, NoStrImplShareTLV.SALT); + tapeStudio.recordByteArrayFor(nonce, NoStrImplShareTLV.NONCE); + tapeStudio.recordByteFor(level, NoStrImplShareTLV.LEVEL); + }); + } + + public byte[] getCryptKey() { + return getTapeDeck().playSpecial().get().array(); + } + + public byte getVersion() { + return getTapeDeck().playByteOf(NoStrImplShareTLV.VERSION).get(); + } + + public byte getLogN() { + return getTapeDeck().playByteOf(NoStrImplShareTLV.LOG_N).get(); + } + + public byte[] getSalt() { + return getTapeDeck().playByteBufferOf(NoStrImplShareTLV.SALT).get().array(); + } + + public byte[] getNonce() { + return getTapeDeck().playByteBufferOf(NoStrImplShareTLV.NONCE).get().array(); + } + + public byte getSecurityLevel() { + return getTapeDeck().playByteOf(NoStrImplShareTLV.LEVEL).get(); + } + + public static NoStrShareLinkPrivateKeySecure ofB32(String linkB32) { + return new NoStrShareLinkPrivateKeySecure(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkProfile.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkProfile.java new file mode 100644 index 0000000..bd16874 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkProfile.java @@ -0,0 +1,33 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.util.List; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public final class NoStrShareLinkProfile extends NoStrShareB32LinkTLV { + + public NoStrShareLinkProfile(OctoBech32String link) { + super(NoStrImplShareLink.PROFILE, link); + } + + public NoStrShareLinkProfile(NoStrIdentityPublicKey publicKey, List relays) { + super(NoStrImplShareLink.PROFILE, tapeStudio -> { + tapeStudio.recordSpecial(publicKey.getBytes()); + tapeStudio.recordRelays(relays); + }); + } + + public NoStrIdentityPublicKey getPublicKey() { + return new NoStrIdentityPublicKey(getTapeDeck().playSpecial().get().array()); + } + + public List getRelays() { + return getTapeDeck().playRelays(); + } + + public static NoStrShareLinkProfile ofB32(String linkB32) { + return new NoStrShareLinkProfile(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPublicKey.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPublicKey.java new file mode 100644 index 0000000..8b6970d --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPublicKey.java @@ -0,0 +1,24 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public final class NoStrShareLinkPublicKey extends NoStrShareB32LinkBare { + + public NoStrShareLinkPublicKey(OctoBech32String link) { + super(NoStrImplShareLink.PUBLIC_KEY, link); + } + + public NoStrShareLinkPublicKey(NoStrIdentityPublicKey publicKey) { + super(NoStrImplShareLink.PUBLIC_KEY, publicKey.getBytes()); + } + + public NoStrIdentityPublicKey getPublicKey() { + return new NoStrIdentityPublicKey(getBareValue()); + } + + public static NoStrShareLinkPublicKey ofB32(String linkB32) { + return new NoStrShareLinkPublicKey(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkRelay.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkRelay.java new file mode 100644 index 0000000..36c6e71 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkRelay.java @@ -0,0 +1,26 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplShareLink; +import love.distributedrebirth.no2all.octo.OctoBech32String; +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public final class NoStrShareLinkRelay extends NoStrShareB32LinkTLV { + + public NoStrShareLinkRelay(OctoBech32String link) { + super(NoStrImplShareLink.RELAY, link); + } + + public NoStrShareLinkRelay(String relayUrl) { + super(NoStrImplShareLink.RELAY, tapeStudio -> { + tapeStudio.recordSpecial(relayUrl, OctoBitFormat.UTF8); + }); + } + + public String getRelayUrl() { + return getTapeDeck().playSpecial(OctoBitFormat.UTF8).get(); + } + + public static NoStrShareLinkRelay ofB32(String linkB32) { + return new NoStrShareLinkRelay(OctoBech32String.of(linkB32)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagA.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagA.java new file mode 100644 index 0000000..b0373b1 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagA.java @@ -0,0 +1,76 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKind; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagA; + +public final class NoStrTagA extends NoStrEventTagBase { + + public NoStrTagA(String address, String relayUrl, Optional platform) { + super(NoStrImplEventTag.A); + setMetaArgumentStr(NoStrImplTagA.ADDRESS, address); + setMetaArgumentStr(NoStrImplTagA.RELAY_URL, relayUrl); + setMetaArgumentOptStr(NoStrImplTagA.PLATFORM, platform); + } + + public NoStrTagA(String[] args) { + super(NoStrImplEventTag.A, args); + } + + public String getAddress() { + return getMetaArgument(NoStrImplTagA.ADDRESS).get(); + } + + public String getRelayUrl() { + return getMetaArgument(NoStrImplTagA.RELAY_URL).get(); + } + + public Optional getPlatform() { + return getMetaArgument(NoStrImplTagA.PLATFORM); + } + + public NoStrEventKind getAddressKind() { + return NoStrEventKind.valueOf(NoStrImplTagA.splitToKind(getAddress())); + } + + public NoStrIdentityPublicKey getAddressPublicKey() { + return NoStrIdentityPublicKey.ofHex(NoStrImplTagA.splitToPublicKey(getAddress())); + } + + public String getAddressParameter() { + return NoStrImplTagA.splitToParameter(getAddress()); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagA(String address, String relayUrl, Optional platform) { + return tag(new NoStrTagA(address, relayUrl, platform)); + } + + default NoStrFireStackWelder tagA(String address, String relayUrl, String platform) { + return tagA(address, relayUrl, Optional.of(platform)); + } + + default NoStrFireStackWelder tagA(String address, String relayUrl) { + return tagA(address, relayUrl, Optional.empty()); + } + + default NoStrFireStackWelder tagA(NoStrEventKind kind, NoStrIdentityPublicKey publicKey, String parameter, String relayUrl, Optional platform) { + return tagA(kind.getNumber() + NoStrImplTagA.SEPERATOR + publicKey.getHex() + NoStrImplTagA.SEPERATOR + parameter, relayUrl, platform); + } + + default NoStrFireStackWelder tagA(NoStrEventKind kind, NoStrIdentityPublicKey publicKey, String parameter, String relayUrl, String platform) { + return tagA(kind, publicKey, parameter, relayUrl, Optional.of(platform)); + } + + default NoStrFireStackWelder tagA(NoStrEventKind kind, NoStrIdentityPublicKey publicKey, String parameter, String relayUrl) { + return tagA(kind, publicKey, parameter, relayUrl, Optional.empty()); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagAlt.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagAlt.java new file mode 100644 index 0000000..e62c637 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagAlt.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagAlt extends NoStrEventTagBaseMono { + + public NoStrTagAlt(String altDescription) { + super(NoStrImplEventTag.ALT); + setMetaArgumentStr(altDescription); + } + + public NoStrTagAlt(String[] args) { + super(NoStrImplEventTag.ALT, args); + } + + public String getAltDescription() { + return getMetaArgument().get(); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagAlt(String altDescription) { + return tag(new NoStrTagAlt(altDescription)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagClient.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagClient.java new file mode 100644 index 0000000..7cf3201 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagClient.java @@ -0,0 +1,45 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagClient; + +public final class NoStrTagClient extends NoStrEventTagBase { + + public NoStrTagClient(String userAgent, Optional version) { + super(NoStrImplEventTag.CLIENT); + setMetaArgumentStr(NoStrImplTagClient.USER_AGENT, userAgent); + setMetaArgumentOptStr(NoStrImplTagClient.VERSION, version); + } + + public NoStrTagClient(String[] args) { + super(NoStrImplEventTag.CLIENT, args); + } + + public String getUserAgent() { + return getMetaArgument(NoStrImplTagClient.USER_AGENT).get(); + } + + public Optional getVersion() { + return getMetaArgument(NoStrImplTagClient.VERSION); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagClient(String userAgent, Optional version) { + return tag(new NoStrTagClient(userAgent, version)); + } + + default NoStrFireStackWelder tagClient(String userAgent, String version) { + return tagClient(userAgent, Optional.of(version)); + } + + default NoStrFireStackWelder tagClient(String userAgent) { + return tagClient(userAgent, Optional.empty()); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagContentWarning.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagContentWarning.java new file mode 100644 index 0000000..9c273ff --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagContentWarning.java @@ -0,0 +1,50 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagContentWarning; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagContentWarningRGroup; + +public final class NoStrTagContentWarning extends NoStrEventTagBase { + + public NoStrTagContentWarning(Optional reason, Optional rateGroup) { + super(NoStrImplEventTag.CONTENT_WARNING); + setMetaArgumentOptStr(NoStrImplTagContentWarning.REASON, reason); + setMetaArgumentOptQName(NoStrImplTagContentWarning.RATE_GROUP, rateGroup); + } + + public NoStrTagContentWarning(String[] args) { + super(NoStrImplEventTag.CONTENT_WARNING, args); + } + + public Optional getReason() { + return getMetaArgument(NoStrImplTagContentWarning.REASON); + } + + public Optional getRateGroup() { + return getMetaArgument(NoStrImplTagContentWarning.RATE_GROUP).flatMap(v -> NoStrImplTagContentWarningRGroup.valueOfQName(v)); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagContentWarning(Optional reason, Optional rateGroup) { + return tag(new NoStrTagContentWarning(reason, rateGroup)); + } + + default NoStrFireStackWelder tagContentWarning(String reason, NoStrImplTagContentWarningRGroup rateGroup) { + return tagContentWarning(Optional.of(reason), Optional.of(rateGroup)); + } + + default NoStrFireStackWelder tagContentWarning(String reason) { + return tagContentWarning(Optional.of(reason), Optional.empty()); + } + + default NoStrFireStackWelder tagContentWarning() { + return tagContentWarning(Optional.empty(), Optional.empty()); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagD.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagD.java new file mode 100644 index 0000000..a50dd5f --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagD.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagD extends NoStrEventTagBaseMono { + + public NoStrTagD(String parameter) { + super(NoStrImplEventTag.D); + setMetaArgumentStr(parameter); + } + + public NoStrTagD(String[] args) { + super(NoStrImplEventTag.D, args); + } + + public String getParameter() { + return getMetaArgument().get(); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagD(String parameter) { + return tag(new NoStrTagD(parameter)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagDescription.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagDescription.java new file mode 100644 index 0000000..32d9653 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagDescription.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagDescription extends NoStrEventTagBaseMono { + + public NoStrTagDescription(String description) { + super(NoStrImplEventTag.DESCRIPTION); + setMetaArgumentStr(description); + } + + public NoStrTagDescription(String[] args) { + super(NoStrImplEventTag.DESCRIPTION, args); + } + + public String getDescription() { + return getMetaArgument().get(); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagDescription(String description) { + return tag(new NoStrTagDescription(description)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagE.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagE.java new file mode 100644 index 0000000..a890a9c --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagE.java @@ -0,0 +1,56 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventId; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagE; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagEMarker; + +public final class NoStrTagE extends NoStrEventTagBase { + + public NoStrTagE(NoStrEventId eventId, Optional relayUrl, Optional marker) { + super(NoStrImplEventTag.E); + setMetaArgumentStr(NoStrImplTagE.EVENT_ID, eventId.getHex()); + setMetaArgumentOptStr(NoStrImplTagE.RELAY_URL, relayUrl); + setMetaArgumentOptQName(NoStrImplTagE.MARKER, marker); + } + + public NoStrTagE(String[] args) { + super(NoStrImplEventTag.E, args); + } + + public NoStrEventId getEventId() { + return NoStrEventId.ofHex(getMetaArgument(NoStrImplTagE.EVENT_ID).get()); + } + + public Optional getRelayUrl() { + return getMetaArgument(NoStrImplTagE.RELAY_URL); + } + + public Optional getMarker() { + return getMetaArgument(NoStrImplTagE.MARKER).flatMap(v -> NoStrImplTagEMarker.valueOfQName(v)); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagE(NoStrEventId eventId, Optional relayUrl, Optional marker) { + return tag(new NoStrTagE(eventId, relayUrl, marker)); + } + + default NoStrFireStackWelder tagE(NoStrEventId eventId, String relayUrl, NoStrImplTagEMarker marker) { + return tagE(eventId, Optional.of(relayUrl), Optional.of(marker)); + } + + default NoStrFireStackWelder tagERelayUrl(NoStrEventId eventId, String relayUrl) { + return tagE(eventId, Optional.of(relayUrl), Optional.empty()); + } + + default NoStrFireStackWelder tagEMarker(NoStrEventId eventId, NoStrImplTagEMarker marker) { + return tagE(eventId, Optional.empty(), Optional.of(marker)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagI.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagI.java new file mode 100644 index 0000000..5d6ece0 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagI.java @@ -0,0 +1,50 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagI; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagIClaim; + +public final class NoStrTagI extends NoStrEventTagBase { + + public NoStrTagI(String platformIdentity, String proof) { + super(NoStrImplEventTag.I); + setMetaArgumentStr(NoStrImplTagI.PLATFORM_IDENTITY, platformIdentity); + setMetaArgumentStr(NoStrImplTagI.PROOF, proof); + } + + public NoStrTagI(String[] args) { + super(NoStrImplEventTag.I, args); + } + + public String getPlatformIdentity() { + return getMetaArgument(NoStrImplTagI.PLATFORM_IDENTITY).get(); + } + + public String getProof() { + return getMetaArgument(NoStrImplTagI.PROOF).get(); + } + + public String getIdentityName() { + return NoStrImplTagIClaim.splitFromPlatformIdentity(getPlatformIdentity()); + } + + public Optional getIdentityClaim() { + return NoStrImplTagIClaim.valueOfPlatformIdentity(getPlatformIdentity()); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagI(String platformIdentity, String proof) { + return tag(new NoStrTagI(platformIdentity, proof)); + } + + default NoStrFireStackWelder tagI(NoStrImplTagIClaim claimPlatform, String claimIdentity, String proof) { + return tagI(claimPlatform.getQName() + NoStrImplTagIClaim.SEPERATOR + claimIdentity, proof); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagImage.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagImage.java new file mode 100644 index 0000000..6a60579 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagImage.java @@ -0,0 +1,57 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagImage; + +public final class NoStrTagImage extends NoStrEventTagBase { + + public NoStrTagImage(String url, Optional dimension) { + super(NoStrImplEventTag.IMAGE); + setMetaArgumentStr(NoStrImplTagImage.URL, url); + setMetaArgumentOptStr(NoStrImplTagImage.DIMENSION, dimension); + } + + public NoStrTagImage(String[] args) { + super(NoStrImplEventTag.IMAGE, args); + } + + public String getUrl() { + return getMetaArgument(NoStrImplTagImage.URL).get(); + } + + public Optional getDimension() { + return getMetaArgument(NoStrImplTagImage.DIMENSION); + } + + public Optional getDimensionWidth() { + return getDimension().map(v -> NoStrImplTagImage.splitDimensionToWidth(v)); + } + + public Optional getDimensionHeight() { + return getDimension().map(v -> NoStrImplTagImage.splitDimensionToHeight(v)); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagImage(String url, Optional dimension) { + return tag(new NoStrTagImage(url, dimension)); + } + + default NoStrFireStackWelder tagImage(String url) { + return tagImage(url, Optional.empty()); + } + + default NoStrFireStackWelder tagImage(String url, String dimension) { + return tagImage(url, Optional.of(dimension)); + } + + default NoStrFireStackWelder tagImage(String url, int width, int height) { + return tagImage(url, Optional.of(NoStrImplTagImage.createDimension(width, height))); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagName.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagName.java new file mode 100644 index 0000000..da58f7c --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagName.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagName extends NoStrEventTagBaseMono { + + public NoStrTagName(String name) { + super(NoStrImplEventTag.NAME); + setMetaArgumentStr(name); + } + + public NoStrTagName(String[] args) { + super(NoStrImplEventTag.NAME, args); + } + + public String getName() { + return getMetaArgument().get(); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagName(String name) { + return tag(new NoStrTagName(name)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagNonce.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagNonce.java new file mode 100644 index 0000000..10eb592 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagNonce.java @@ -0,0 +1,39 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagNonce; + +public final class NoStrTagNonce extends NoStrEventTagBase { + + public NoStrTagNonce(int difficulty) { + super(NoStrImplEventTag.NONCE); + setMetaArgumentStr(NoStrImplTagNonce.BEARER_PROOF, Integer.toString(NoStrImplTagNonce.DEFAULT_BEARER_PROOF)); + setMetaArgumentStr(NoStrImplTagNonce.DIFFICULTY, Integer.toString(difficulty)); + } + + public NoStrTagNonce(String[] args) { + super(NoStrImplEventTag.NONCE, args); + } + + public void setBearerProof(int bearerProof) { + setMetaArgumentStr(NoStrImplTagNonce.BEARER_PROOF, Integer.toString(bearerProof)); + } + + public int getBearerProof() { + return Integer.valueOf(getMetaArgument(NoStrImplTagNonce.BEARER_PROOF).get()); + } + + public int getDifficulty() { + return Integer.valueOf(getMetaArgument(NoStrImplTagNonce.DIFFICULTY).get()); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagNonce(int difficulty) { + return tag(new NoStrTagNonce(difficulty)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagP.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagP.java new file mode 100644 index 0000000..7fd7d2e --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagP.java @@ -0,0 +1,54 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagP; + +public final class NoStrTagP extends NoStrEventTagBase { + + public NoStrTagP(NoStrIdentityPublicKey publicKey, Optional relayUrl, Optional petName) { + super(NoStrImplEventTag.P); + setMetaArgumentStr(NoStrImplTagP.PUBLIC_KEY, publicKey.getHex()); + setMetaArgumentOptStr(NoStrImplTagP.PET_NAME, petName); + } + + public NoStrTagP(String[] args) { + super(NoStrImplEventTag.E, args); + } + + public NoStrIdentityPublicKey getPublicKey() { + return NoStrIdentityPublicKey.ofHex(getMetaArgument(NoStrImplTagP.PUBLIC_KEY).get()); + } + + public Optional getPetName() { + return getMetaArgument(NoStrImplTagP.PET_NAME); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagP(NoStrIdentityPublicKey publicKey, Optional relayUrl, Optional petName) { + return tag(new NoStrTagP(publicKey, relayUrl, petName)); + } + + default NoStrFireStackWelder tagP(NoStrIdentityPublicKey publicKey, String relayUrl, String petName) { + return tagP(publicKey, Optional.of(relayUrl), Optional.of(petName)); + } + + default NoStrFireStackWelder tagP(NoStrIdentityPublicKey publicKey) { + return tagP(publicKey, Optional.empty(), Optional.empty()); + } + + default NoStrFireStackWelder tagPRelayUrl(NoStrIdentityPublicKey publicKey, String relayUrl) { + return tagP(publicKey, Optional.of(relayUrl), Optional.empty()); + } + + default NoStrFireStackWelder tagPPetName(NoStrIdentityPublicKey publicKey, String petName) { + return tagP(publicKey, Optional.empty(), Optional.of(petName)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagPublishedAt.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagPublishedAt.java new file mode 100644 index 0000000..9d3dc14 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagPublishedAt.java @@ -0,0 +1,35 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.time.Instant; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagPublishedAt extends NoStrEventTagBaseMono { + + public NoStrTagPublishedAt(Instant publishedAt) { + super(NoStrImplEventTag.PUBLISHED_AT); + setMetaArgumentStr(Long.toString(publishedAt.getEpochSecond())); + } + + public NoStrTagPublishedAt(String[] args) { + super(NoStrImplEventTag.PUBLISHED_AT, args); + } + + public Instant getPublishedAt() { + return Instant.ofEpochSecond(Long.valueOf(getMetaArgument().get())); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagPublishedAt(Instant publishedAt) { + return tag(new NoStrTagPublishedAt(publishedAt)); + } + + default NoStrFireStackWelder tagPublishedAtNow() { + return tagPublishedAt(Instant.now()); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagR.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagR.java new file mode 100644 index 0000000..8871fed --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagR.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagR extends NoStrEventTagBaseMono { + + public NoStrTagR(String reference) { + super(NoStrImplEventTag.R); + setMetaArgumentStr(reference); + } + + public NoStrTagR(String[] args) { + super(NoStrImplEventTag.R, args); + } + + public String getReference() { + return getMetaArgument().get(); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagR(String reference) { + return tag(new NoStrTagR(reference)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagSubject.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagSubject.java new file mode 100644 index 0000000..20345b6 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagSubject.java @@ -0,0 +1,45 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagSubject; + +public final class NoStrTagSubject extends NoStrEventTagBase { + + public NoStrTagSubject(String subject) { + super(NoStrImplEventTag.SUBJECT); + setMetaArgumentStr(NoStrImplTagSubject.SUBJECT, subject); + } + + public NoStrTagSubject(String[] args) { + super(NoStrImplEventTag.SUBJECT, args); + } + + public String getSubject() { + return getMetaArgument(NoStrImplTagSubject.SUBJECT).get(); + } + + public boolean hasReplyPrefix() { + return NoStrImplTagSubject.hasReplyPrefix(getSubject()); + } + + public boolean hasChoppedPostfix() { + return NoStrImplTagSubject.hasChoppedPostfix(getSubject()); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagSubject(String subject) { + return tag(new NoStrTagSubject(NoStrImplTagSubject.limitLength(subject))); + } + + default NoStrFireStackWelder tagSubjectReply(String subject) { + if (NoStrImplTagSubject.hasReplyPrefix(subject)) { + return tagSubject(subject); + } + return tagSubject(NoStrImplTagSubject.prefixSubject(subject)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagSummary.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagSummary.java new file mode 100644 index 0000000..239956e --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagSummary.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagSummary extends NoStrEventTagBaseMono { + + public NoStrTagSummary(String summary) { + super(NoStrImplEventTag.SUMMARY); + setMetaArgumentStr(summary); + } + + public NoStrTagSummary(String[] args) { + super(NoStrImplEventTag.SUMMARY, args); + } + + public String getSummary() { + return getMetaArgument().get(); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagSummary(String summary) { + return tag(new NoStrTagSummary(summary)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagT.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagT.java new file mode 100644 index 0000000..2290b01 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagT.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBaseMono; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public final class NoStrTagT extends NoStrEventTagBaseMono { + + public NoStrTagT(String hashTag) { + super(NoStrImplEventTag.T); + setMetaArgumentStr(hashTag); + } + + public NoStrTagT(String[] args) { + super(NoStrImplEventTag.T, args); + } + + public String getHashTag() { + return getMetaArgument().get(); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagT(String hashTag) { + return tag(new NoStrTagT(hashTag)); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagThumb.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagThumb.java new file mode 100644 index 0000000..dfbb429 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagThumb.java @@ -0,0 +1,57 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagThumb; + +public final class NoStrTagThumb extends NoStrEventTagBase { + + public NoStrTagThumb(String url, Optional dimension) { + super(NoStrImplEventTag.THUMB); + setMetaArgumentStr(NoStrImplTagThumb.URL, url); + setMetaArgumentOptStr(NoStrImplTagThumb.DIMENSION, dimension); + } + + public NoStrTagThumb(String[] args) { + super(NoStrImplEventTag.THUMB, args); + } + + public String getUrl() { + return getMetaArgument(NoStrImplTagThumb.URL).get(); + } + + public Optional getDimension() { + return getMetaArgument(NoStrImplTagThumb.DIMENSION); + } + + public Optional getDimensionWidth() { + return getDimension().map(v -> NoStrImplTagThumb.splitDimensionToWidth(v)); + } + + public Optional getDimensionHeight() { + return getDimension().map(v -> NoStrImplTagThumb.splitDimensionToHeight(v)); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagThumb(String url, Optional dimension) { + return tag(new NoStrTagThumb(url, dimension)); + } + + default NoStrFireStackWelder tagThumb(String url) { + return tagThumb(url, Optional.empty()); + } + + default NoStrFireStackWelder tagThumb(String url, String dimension) { + return tagThumb(url, Optional.of(dimension)); + } + + default NoStrFireStackWelder tagThumb(String url, int width, int height) { + return tagThumb(url, Optional.of(NoStrImplTagThumb.createDimension(width, height))); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagTitle.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagTitle.java new file mode 100644 index 0000000..eb24e2a --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagTitle.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackFishWelder; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireStackWelder; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagBase; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagTitle; + +public final class NoStrTagTitle extends NoStrEventTagBase { + + public NoStrTagTitle(String title) { + super(NoStrImplEventTag.TITLE); + setMetaArgumentStr(NoStrImplTagTitle.TITLE, title); + } + + public NoStrTagTitle(String[] args) { + super(NoStrImplEventTag.TITLE, args); + } + + public String getTitle() { + return getMetaArgument(NoStrImplTagTitle.TITLE).get(); + } + + public boolean hasChoppedPostfix() { + return NoStrImplTagTitle.hasChoppedPostfix(getTitle()); + } + + public interface FishWelder extends NoStrFireStackFishWelder { + + default NoStrFireStackWelder tagTitle(String title) { + return tag(new NoStrTagTitle(NoStrImplTagTitle.limitLength(title))); + } + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImpl.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImpl.java new file mode 100644 index 0000000..e4f0e9e --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImpl.java @@ -0,0 +1,89 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +public enum NoStrImpl implements NoStrImplPossibility { + + NIP_0_UNKNOWN, + NIP_1_BASE_PROTOCOL, + NIP_2_CONTACTS, + NIP_3_OPEN_TIMESTAMPS, + NIP_4_CRYTE_DIRECT_MSG, + NIP_5_NOSTR_TO_DNS, + NIP_6_KEY_FROM_MNEMONIC_SEED, + NIP_7_BROWSER_WINDOW_NOSTR, + NIP_9_EVENT_DELETION, + NIP_10_CORRECT_BASE_TAGS, + NIP_11_RELAY_INFO_DOC, + NIP_12_GENERIC_TAG_QUERY, + NIP_13_PROOF_OF_WORK, + NIP_14_TEXT_SUBJECT, + NIP_15_MARKETPLACE, + NIP_16_EVENT_TREATMENT, + NIP_18_REPOSTS, + NIP_19_BECH32_ENCODED, + NIP_20_COMMAND_RESULT, + NIP_21_NOSTR_URL_SCHEMA, + NIP_22_EVENT_AT_LIMITS, + NIP_23_LONG_FORM_CONTENT, + NIP_25_REACTIONS, + NIP_26_DELEGATED_EVENT_SIGN, + NIP_27_TEXT_NOTE_REFERENCE, + NIP_28_PUBLIC_CHAT, + NIP_30_CUSTOM_EMOJI, + NIP_31_ALT_FOR_UNKNOWN, + NIP_33_PARA_REPLACEABLE_EVENTS, + NIP_36_SENSITIVE_CONTENT, + NIP_39_EXT_INDENTITIES_IN_PROFILE, + NIP_40_EXPIRATION_TIMESTAMP, + NIP_42_AUTH_RELAY, + NIP_45_COUNTING_RESULTS, + NIP_46_NOSTR_CONNECT, + NIP_47_WALLET_CONNECT, + NIP_50_KEYWORD_FILTER, + NIP_51_LISTS, + NIP_56_REPORTING, + NIP_57_LIGHTING_ZAPS, + NIP_58_BADGES, + NIP_65_RELAY_LIST_METADATA, + NIP_78_APP_CUSTOM_DATA, + @Deprecated + NIP_89_RECOMMENDED_APP_HANDLERS, // used dyna hash tags so client will a cancer disconnect if a mongool send this packet. + NIP_94_FILE_METADATA, + ; + + private static final char PREFIX_SPLIT = '_'; + private static final int PREFIX_LENGTH = 4; + private static final Map MAP_NUMBER = Collections.unmodifiableMap( + Arrays.asList(values()).stream().collect(Collectors.toMap(v -> v.getNumber(), v -> v))); + private final int number; + private final String qFlag; + + private NoStrImpl() { + int secondSplitIdx = name().indexOf(PREFIX_SPLIT, PREFIX_LENGTH); + String numberStr = name().substring(PREFIX_LENGTH, secondSplitIdx); + this.number = Integer.valueOf(numberStr); + this.qFlag = name(); + } + + @Override + public int getNumber() { + return number; + } + + @Override + public String getQFlag() { + return qFlag; + } + + public static boolean valueOfNIPValid(int nip) { + return MAP_NUMBER.containsKey(nip); + } + + public static NoStrImpl valueOfNIP(int nip) { + return MAP_NUMBER.get(nip); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplBinHex.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplBinHex.java new file mode 100644 index 0000000..1ba9f01 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplBinHex.java @@ -0,0 +1,10 @@ +package love.distributedrebirth.no2all.nostr.nip; + +public interface NoStrImplBinHex { + + byte[] getBytes(); + + String getHex(); + + String getHexDipavali(); +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplBinHexAdapter.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplBinHexAdapter.java new file mode 100644 index 0000000..cdef27a --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplBinHexAdapter.java @@ -0,0 +1,33 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Objects; + +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class NoStrImplBinHexAdapter implements NoStrImplBinHex { + + private final byte[] valueBytes; + private final String valueHex; + private final String valueHexDipavali; + + public NoStrImplBinHexAdapter(byte[] value) { + this.valueBytes = Objects.requireNonNull(value); + this.valueHex = OctoBitFormat.HEX.fromBytes(value); + this.valueHexDipavali = OctoBitFormat.HEX_DIPAVALI.fromBytes(value); + } + + @Override + public final byte[] getBytes() { + return valueBytes; + } + + @Override + public final String getHex() { + return valueHex; + } + + @Override + public String getHexDipavali() { + return valueHexDipavali; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplChuch.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplChuch.java new file mode 100644 index 0000000..cda7a43 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplChuch.java @@ -0,0 +1,133 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import jakarta.json.Json; +import jakarta.json.JsonArray; +import jakarta.json.JsonArrayBuilder; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonString; +import jakarta.json.JsonValue; +import love.distributedrebirth.no2all.nostr.NoStrGospel; + +public final class NoStrImplChuch { + + public static void readArrayString(JsonObject json, NoStrImplQName jsonName, List data) { + readArrayString(json, jsonName.getQName(), data); + } + + public static void readArrayString(JsonObject json, String jsonName, List data) { + readArray(json, jsonName, v -> data.add(((JsonString)v).getString())); + } + + public static void readArray(JsonObject json, NoStrImplQName jsonName, Consumer arrayValue) { + readArray(json, jsonName.getQName(), arrayValue); + } + + public static void readArray(JsonObject json, String jsonName, Consumer arrayValue) { + if (!json.containsKey(jsonName)) { + return; + } + JsonArray values = json.getJsonArray(jsonName); + for (JsonValue valueJson : values) { + arrayValue.accept(valueJson); + } + } + + public static String readString(JsonObject json, NoStrImplQName jsonName) { + return readString(json, jsonName.getQName()); + } + + public static String readString(JsonObject json, String jsonName) { + return json.getString(jsonName, null); + } + + public static Integer readInteger(JsonObject json, NoStrImplQName jsonName) { + return readInteger(json, jsonName.getQName()); + } + + public static Integer readInteger(JsonObject json, String jsonName) { + if (json.containsKey(jsonName)) { + return json.getInt(jsonName); + } + return null; + } + + public static Boolean readBoolean(JsonObject json, NoStrImplQName jsonName) { + return readBoolean(json, jsonName.getQName()); + } + + public static Boolean readBoolean(JsonObject json, String jsonName) { + if (json.containsKey(jsonName)) { + return json.getBoolean(jsonName); + } + return null; + } + + public static void writeArrayWriter(JsonObjectBuilder json, NoStrImplQName jsonName, List data) { + writeArrayWriter(json, jsonName.getQName(), data); + } + + public static void writeArrayWriter(JsonObjectBuilder json, String jsonName, List data) { + writeArray(json, jsonName, data, (r, v) -> r.add(v.toBible())); + } + + public static void writeArrayString(JsonObjectBuilder json, NoStrImplQName jsonName, List data) { + writeArrayString(json, jsonName.getQName(), data); + } + + public static void writeArrayString(JsonObjectBuilder json, String jsonName, List data) { + writeArray(json, jsonName, data, (r, v) -> r.add(v)); + } + + public static void writeArray(JsonObjectBuilder json, NoStrImplQName jsonName, List data, BiConsumer addFn) { + writeArray(json, jsonName.getQName(), data, addFn); + } + + public static void writeArray(JsonObjectBuilder json, String jsonName, List data, BiConsumer addFn) { + JsonArrayBuilder builder = Json.createArrayBuilder(); + for (T value : data) { + addFn.accept(builder, value); + } + JsonArray result = builder.build(); + if (!result.isEmpty()) { + json.add(jsonName, result); + } + } + + public static void writeString(JsonObjectBuilder json, NoStrImplQName jsonName, String data) { + writeString(json, jsonName.getQName(), data); + } + + public static void writeString(JsonObjectBuilder json, String jsonName, String data) { + if (data == null) { + return; + } + json.add(jsonName, data); + } + + public static void writeInteger(JsonObjectBuilder json, NoStrImplQName jsonName, Integer data) { + writeInteger(json, jsonName.getQName(), data); + } + + public static void writeInteger(JsonObjectBuilder json, String jsonName, Integer data) { + if (data == null) { + return; + } + json.add(jsonName, data); + } + + public static void writeBoolean(JsonObjectBuilder json, NoStrImplQName jsonName, Boolean data) { + writeBoolean(json, jsonName.getQName(), data); + } + + public static void writeBoolean(JsonObjectBuilder json, String jsonName, Boolean data) { + if (data == null) { + return; + } + json.add(jsonName, data); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKind.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKind.java new file mode 100644 index 0000000..c28ede3 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKind.java @@ -0,0 +1,93 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKind; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKindRange; + +public enum NoStrImplEventKind implements NoStrEventKind { + + METADATA (0, NoStrImpl.NIP_1_BASE_PROTOCOL), + TEXT_NOTE (1, NoStrImpl.NIP_1_BASE_PROTOCOL), + RECOMMEND_RELAY (2, NoStrImpl.NIP_1_BASE_PROTOCOL), + CONTACTS (3, NoStrImpl.NIP_2_CONTACTS), + CRYPT_DIRECT_MSG (4, NoStrImpl.NIP_4_CRYTE_DIRECT_MSG), + EVENT_DELETION (5, NoStrImpl.NIP_9_EVENT_DELETION), + REPOSTS (6, NoStrImpl.NIP_18_REPOSTS), + REACTION (7, NoStrImpl.NIP_25_REACTIONS), + BADGE_AWARD (8, NoStrImpl.NIP_58_BADGES), + + CHANNEL_CREATION (40, NoStrImpl.NIP_28_PUBLIC_CHAT), + CHANNEL_METADATA (41, NoStrImpl.NIP_28_PUBLIC_CHAT), + CHANNEL_SEND_MSG (42, NoStrImpl.NIP_28_PUBLIC_CHAT), + CHANNEL_HIDE_MSG (43, NoStrImpl.NIP_28_PUBLIC_CHAT), + CHANNEL_MUTE_USER (44, NoStrImpl.NIP_28_PUBLIC_CHAT), + + FILE_METADATA (1063, NoStrImpl.NIP_94_FILE_METADATA), + REPORTING (1984, NoStrImpl.NIP_56_REPORTING), + ZAP_REQUEST (9734, NoStrImpl.NIP_57_LIGHTING_ZAPS), + ZAP_TX (9735, NoStrImpl.NIP_57_LIGHTING_ZAPS), + + MUTE_LIST (10000, NoStrImpl.NIP_51_LISTS), + PIN_LIST (10001, NoStrImpl.NIP_51_LISTS), + RELAY_LIST_METADATA (10002, NoStrImpl.NIP_65_RELAY_LIST_METADATA), + WALLET_INFO (13194, NoStrImpl.NIP_47_WALLET_CONNECT), + + CLIENT_AUTH (22242, NoStrImpl.NIP_42_AUTH_RELAY), + WALLET_REQUEST (23194, NoStrImpl.NIP_47_WALLET_CONNECT), + WALLET_RESPONSE (23195, NoStrImpl.NIP_47_WALLET_CONNECT), + NOSTR_CONNECT (24133, NoStrImpl.NIP_46_NOSTR_CONNECT), + + CAT_PEOPLE_LIST (30000, NoStrImpl.NIP_51_LISTS), + CAT_BOOKMARK_LIST (30001, NoStrImpl.NIP_51_LISTS), + PROFILE_BADGES (30008, NoStrImpl.NIP_58_BADGES), + BADGE_DEFINITION (30009, NoStrImpl.NIP_58_BADGES), + MARKET_STALL (30017, NoStrImpl.NIP_15_MARKETPLACE), + MARKET_PRODUCT (30018, NoStrImpl.NIP_15_MARKETPLACE), + LONG_FORM_CONTENT (30023, NoStrImpl.NIP_23_LONG_FORM_CONTENT), + APP_CUSTOM_DATA (30078, NoStrImpl.NIP_78_APP_CUSTOM_DATA), + ; + + private static final Map MAP_NUMBER = Collections.unmodifiableMap( + Arrays.asList(values()).stream().collect(Collectors.toMap(v -> v.getNumber(), v -> v))); + private final int number; + private final NoStrImplPossibility nip; + private final NoStrEventKindRange range; + + private NoStrImplEventKind(int number, NoStrImpl nip) { + this.number = number; + this.nip = nip; + this.range = NoStrImplEventKindRange.valueOfKind(number); + } + + @Override + public int getNumber() { + return number; + } + + @Override + public String getQFlag() { + return name(); + } + + @Override + public NoStrImplPossibility getNip() { + return nip; + } + + @Override + public NoStrEventKindRange getRange() { + return range; + } + + public static boolean valueOfKindValid(int kind) { + return MAP_NUMBER.containsKey(kind); + } + + public static NoStrImplEventKind valueOfKind(int kind) { + return MAP_NUMBER.get(kind); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindRange.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindRange.java new file mode 100644 index 0000000..4d4092f --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindRange.java @@ -0,0 +1,63 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventKindRange; + +public enum NoStrImplEventKindRange implements NoStrEventKindRange { + + // Internal Nips + UNKNOWN ( -1, -1), + CHAT ( 0, 999), + REGULAR ( 1000, 9999), + REGULAR_REPL (10000, 19999), + EPHEMERAL (20000, 29999), + PARAM_REPL (30000, 39999), + // External Apps + NOSTROCKET (640000,649999), + ; + + private final int numberStart; + private final int numberstop; + + private NoStrImplEventKindRange(int numberStart, int numberstop) { + this.numberStart = numberStart; + this.numberstop = numberstop; + } + + @Override + public String getQFlag() { + return name(); + } + + @Override + public int getNumberStart() { + return numberStart; + } + + @Override + public int getNumberstop() { + return numberstop; + } + + @Override + public boolean isReplaceable(int number) { + if (number == 0 || number == 3 || number == 41) { + return true; + } + if (this == REGULAR_REPL) { + return true; + } + if (this == PARAM_REPL) { + return true; + } + return false; + } + + public static NoStrImplEventKindRange valueOfKind(int number) { + for (NoStrImplEventKindRange range : values()) { + if (number >= range.getNumberStart() && number <= range.getNumberstop()) { + return range; + } + } + return NoStrImplEventKindRange.UNKNOWN; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventTag.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventTag.java new file mode 100644 index 0000000..83e0a18 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventTag.java @@ -0,0 +1,112 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventTagMeta; + +public enum NoStrImplEventTag implements NoStrEventTagMeta { + + // the "p" tag is used for referencing public keys + P (NoStrImpl.NIP_1_BASE_PROTOCOL), + // the "e" tag for referencing event ids + E (NoStrImpl.NIP_1_BASE_PROTOCOL, NoStrImpl.NIP_10_CORRECT_BASE_TAGS), + + // ["a", "::", ""] + A (NoStrImpl.NIP_23_LONG_FORM_CONTENT, NoStrImpl.NIP_33_PARA_REPLACEABLE_EVENTS), + I (NoStrImpl.NIP_39_EXT_INDENTITIES_IN_PROFILE), + // the "d" tag is a delete slug key for "pubkey:kind:d" + time, empty is def key + D (NoStrImpl.NIP_33_PARA_REPLACEABLE_EVENTS), // TODO: add single tag info + check + T (NoStrImpl.NIP_12_GENERIC_TAG_QUERY), + R (NoStrImpl.NIP_12_GENERIC_TAG_QUERY), + G (NoStrImpl.NIP_12_GENERIC_TAG_QUERY), + + CHALLENGE (NoStrImpl.NIP_42_AUTH_RELAY), + RELAY (NoStrImpl.NIP_42_AUTH_RELAY), + + TITLE (NoStrImpl.NIP_23_LONG_FORM_CONTENT), + SUMMARY (NoStrImpl.NIP_23_LONG_FORM_CONTENT), + PUBLISHED_AT (NoStrImpl.NIP_23_LONG_FORM_CONTENT), + IMAGE (NoStrImpl.NIP_23_LONG_FORM_CONTENT, NoStrImpl.NIP_58_BADGES), + + ZAP (NoStrImpl.NIP_57_LIGHTING_ZAPS), + LNURL (NoStrImpl.NIP_57_LIGHTING_ZAPS), + AMOUNT (NoStrImpl.NIP_57_LIGHTING_ZAPS), + BOLT11 (NoStrImpl.NIP_57_LIGHTING_ZAPS), + PREIMAGE (NoStrImpl.NIP_57_LIGHTING_ZAPS), + DESCRIPTION (NoStrImpl.NIP_57_LIGHTING_ZAPS, NoStrImpl.NIP_58_BADGES), + RELAYS (NoStrImpl.NIP_57_LIGHTING_ZAPS), + + CLIENT (true, NoStrImpl.NIP_0_UNKNOWN), + NONCE (true, NoStrImpl.NIP_13_PROOF_OF_WORK), + SUBJECT (NoStrImpl.NIP_14_TEXT_SUBJECT), + DELEGATION (NoStrImpl.NIP_26_DELEGATED_EVENT_SIGN), + EMOJI (NoStrImpl.NIP_30_CUSTOM_EMOJI), + CONTENT_WARNING (NoStrImpl.NIP_36_SENSITIVE_CONTENT), + EXPIRATION (NoStrImpl.NIP_40_EXPIRATION_TIMESTAMP), + ALT (NoStrImpl.NIP_31_ALT_FOR_UNKNOWN), + NAME (NoStrImpl.NIP_58_BADGES), + THUMB (NoStrImpl.NIP_58_BADGES), + + // Dat Package extensions + DPFH (NoStrImpl.NIP_0_UNKNOWN), // "DAT Package File Hash" with event content = maven://ndaturl+versionhash + ; + + private static final Map MAP_QNAME = Collections.unmodifiableMap( + Arrays.asList(values()).stream().collect(Collectors.toMap(v -> v.getQName(), v -> v))); + private final String jsonQName; + private final NoStrImplPossibility[] nips; + private final boolean quariable; + private final boolean singleton; + + private NoStrImplEventTag(NoStrImpl... nips) { + this(false, nips); // TODO: add info on all needed tags + } + + private NoStrImplEventTag(boolean singleton, NoStrImpl... nips) { + this.jsonQName = name().toLowerCase().replaceAll("_", "-"); + this.nips = nips; + this.quariable = isQuariable(name()); + this.singleton = singleton; + } + + @Override + public String getQName() { + return jsonQName; + } + + @Override + public String getQFlag() { + return name(); + } + + @Override + public NoStrImplPossibility[] getNips() { + return nips; + } + + @Override + public boolean isQuariable() { + return quariable; + } + + @Override + public boolean isSinglton() { + return singleton; + } + + public static boolean isQuariable(String qName) { + return qName != null && qName.length() == 1; + } + + public static boolean valueOfWireValid(String tag) { + return MAP_QNAME.containsKey(Objects.requireNonNull(tag)); + } + + public static NoStrEventTagMeta valueOfWire(String tag) { + return MAP_QNAME.get(Objects.requireNonNull(tag)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventTagArgument.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventTagArgument.java new file mode 100644 index 0000000..76984b5 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventTagArgument.java @@ -0,0 +1,6 @@ +package love.distributedrebirth.no2all.nostr.nip; + +public interface NoStrImplEventTagArgument { + + int ordinal(); +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageOkReason.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageOkReason.java new file mode 100644 index 0000000..814dc87 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageOkReason.java @@ -0,0 +1,43 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Objects; + +public enum NoStrImplMessageOkReason { + + DEFAULT (""), + BLOCKED ("blocked:"), + INVALID ("invalid:"), + POW ("pow:"), + RATE_LIMITED ("rate-limited:"), + ERROR ("error:"), + ; + + private final String prefix; + private final String prefixFormat; + + private NoStrImplMessageOkReason(String prefix) { + this.prefix = prefix; + this.prefixFormat = prefix + (prefix.isEmpty()?"":" "); + } + + public String getPrefix() { + return prefix; + } + + public String format(String message) { + return prefixFormat + Objects.requireNonNull(message); + } + + public static NoStrImplMessageOkReason valueOfPrefix(String message) { + Objects.requireNonNull(message); + for (NoStrImplMessageOkReason reason : values()) { + if (reason == DEFAULT) { + continue; + } + if (message.startsWith(reason.getPrefix())) { + return reason; + } + } + return NoStrImplMessageOkReason.DEFAULT; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageType.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageType.java new file mode 100644 index 0000000..5c8dace --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageType.java @@ -0,0 +1,70 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import jakarta.json.JsonArray; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClient; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelay; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientAuth; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientCount; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientEose; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientEvent; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientNotice; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientOk; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayAuth; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayClose; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayCount; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayEvent; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayReq; + +public enum NoStrImplMessageType implements NoStrImplQName { + + EVENT (NoStrImpl.NIP_1_BASE_PROTOCOL, v -> new NoStrMsgToRelayEvent(v), v -> new NoStrMsgToClientEvent(v)), + REQ (NoStrImpl.NIP_1_BASE_PROTOCOL, v -> new NoStrMsgToRelayReq(v), null), + CLOSE (NoStrImpl.NIP_1_BASE_PROTOCOL, v -> new NoStrMsgToRelayClose(v), null), + EOSE (NoStrImpl.NIP_1_BASE_PROTOCOL, null, v -> new NoStrMsgToClientEose(v)), + NOTICE (NoStrImpl.NIP_1_BASE_PROTOCOL, null, v -> new NoStrMsgToClientNotice(v)), + + OK (NoStrImpl.NIP_20_COMMAND_RESULT, null, v -> new NoStrMsgToClientOk(v)), + AUTH (NoStrImpl.NIP_42_AUTH_RELAY, v -> new NoStrMsgToRelayAuth(v), v -> new NoStrMsgToClientAuth(v)), + COUNT (NoStrImpl.NIP_45_COUNTING_RESULTS, v -> new NoStrMsgToRelayCount(v), v -> new NoStrMsgToClientCount(v)), + ; + + private static final Set MAP_NAME = Arrays.asList(values()).stream().map(v -> v.name()).collect(Collectors.toSet()); + private final NoStrImplPossibility requireNip; + private final Optional> mapperToRelay; + private final Optional> mapperToClient; + + private NoStrImplMessageType(NoStrImpl requireNip, + Function mapperToRelay, + Function mapperToClient) { + this.requireNip = requireNip; + this.mapperToRelay = Optional.ofNullable(mapperToRelay); + this.mapperToClient = Optional.ofNullable(mapperToClient); + } + + public NoStrImplPossibility getFeatureNip() { + return requireNip; + } + + public String getQName() { + return name(); + } + + public Optional> getMapperToRelay() { + return mapperToRelay; + } + + public Optional> getMapperToClient() { + return mapperToClient; + } + + public static boolean valueOfValid(String name) { + return MAP_NAME.contains(Objects.requireNonNull(name)); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplPossibility.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplPossibility.java new file mode 100644 index 0000000..75bd1c8 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplPossibility.java @@ -0,0 +1,6 @@ +package love.distributedrebirth.no2all.nostr.nip; + +public interface NoStrImplPossibility extends NoStrImplQFlag { + + int getNumber(); +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplQFlag.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplQFlag.java new file mode 100644 index 0000000..2d4cce4 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplQFlag.java @@ -0,0 +1,8 @@ +package love.distributedrebirth.no2all.nostr.nip; + +public interface NoStrImplQFlag { + + String CUSTOM_INDICATOR = "CUSTOM"; + + String getQFlag(); // for part of i18n key slug +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplQName.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplQName.java new file mode 100644 index 0000000..b8195e0 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplQName.java @@ -0,0 +1,17 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Optional; + +public interface NoStrImplQName { + + String getQName(); // for gospel church bible + + static Optional valueOfQName(T[] values, String qName) { + for (T value : values) { + if (value.getQName().equals(qName)) { + return Optional.of(value); + } + } + return Optional.empty(); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplShareLink.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplShareLink.java new file mode 100644 index 0000000..e05aa22 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplShareLink.java @@ -0,0 +1,52 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import java.util.Objects; + +import love.distributedrebirth.no2all.octo.OctoBech32; +import love.distributedrebirth.no2all.octo.OctoBech32String; + +public enum NoStrImplShareLink { // TODO: add NIP dep + // bare value bach32 + PRIVATE_KEY("nsec"), + PUBLIC_KEY("npub"), + NOTE("note"), + // TLV values bach32 + PROFILE("nprofile"), + EVENT("nevent"), + RELAY("nrelay"), + EVENT_REFERENCE("naddr"), + //CHANNEL("nchannel"), //todo find docs + + PRIVATE_KEY_SECURE("ncryptsec"), // bip 49 +// PRIVATE_KEY_SECURE("nsecpw"), + +// SECURE_URL("nsurl"), + ; + + private final String prefix; + + private NoStrImplShareLink(String prefix) { + this.prefix = prefix; + } + + public String getPrefix() { + return prefix; + } + + public OctoBech32String validate(OctoBech32String link) { + Objects.requireNonNull(link); + Objects.requireNonNull(link.getValue()); + if (link.getValue().startsWith(getPrefix())) { + return link; + } + throw new IllegalArgumentException("Wrong prefix; wanted " + getPrefix() + " got " + link.getValue()); + } + + public byte[] fromBech32(OctoBech32String link) { + return OctoBech32.fromBech32(validate(link)).getData(); + } + + public String toBech32(byte[] data) { + return OctoBech32.toBech32(getPrefix(), data).getValue(); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplShareTLV.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplShareTLV.java new file mode 100644 index 0000000..8c4ff4f --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplShareTLV.java @@ -0,0 +1,41 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import love.distributedrebirth.no2all.octo.OctoBitChainFrameType; + +public enum NoStrImplShareTLV implements OctoBitChainFrameType { + + SPECIAL(0), // pubkey/eventId/payload/etc + RELAY(1), + AUTHOR(2), + KIND(3), + + // new + CLIENT(4), // for appId + + // for "ncryptsec" ChaCha20-Poly1305 CIPHERTEXT is in SPECIAL + VERSION(5), // one byte + LOG_N(6), // one byte + SALT(7), // 16 bytes random + NONCE(8), // 24 bytes from ChaCha20-Poly1305 + LEVEL(9), // 1 byte to indicate security level of private key + + // for "nsecpw" (simple AES) + //SALT, + //IV + + // for "nsurl" + //SURL, + //PKCS_8, // (full ASN.1 DER packet) + ; + + private final byte typeByte; + + private NoStrImplShareTLV(int typeByte) { + this.typeByte = (byte)typeByte; + } + + @Override + public byte getTypeByte() { + return typeByte; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagA.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagA.java new file mode 100644 index 0000000..a297440 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagA.java @@ -0,0 +1,32 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagA implements NoStrImplEventTagArgument { + ADDRESS, + RELAY_URL, + PLATFORM, + ; + + public static final String SEPERATOR = ":"; + + public static int splitToKind(String address) { + int idxSep = address.indexOf(SEPERATOR); + String result = address.substring(0, idxSep); + return Integer.valueOf(result); + } + + public static String splitToPublicKey(String address) { + int idxSep = address.indexOf(SEPERATOR); + int idxSepEnd = address.indexOf(SEPERATOR, idxSep + 1); + String result = address.substring(idxSep + 1, idxSepEnd); + return result; + } + + public static String splitToParameter(String address) { + int idxSep = address.indexOf(SEPERATOR); + int idxSepEnd = address.indexOf(SEPERATOR, idxSep + 1); + String result = address.substring(idxSepEnd + 1, address.length()); + return result; + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagClient.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagClient.java new file mode 100644 index 0000000..bd334df --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagClient.java @@ -0,0 +1,9 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagClient implements NoStrImplEventTagArgument { + USER_AGENT, + VERSION, + ; +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagContentWarning.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagContentWarning.java new file mode 100644 index 0000000..dc71f1d --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagContentWarning.java @@ -0,0 +1,9 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagContentWarning implements NoStrImplEventTagArgument { + REASON, + RATE_GROUP + ; +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagContentWarningRGroup.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagContentWarningRGroup.java new file mode 100644 index 0000000..ede5f5f --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagContentWarningRGroup.java @@ -0,0 +1,30 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public enum NoStrImplTagContentWarningRGroup implements NoStrImplQName { + // from: https://en.wikipedia.org/wiki/Motion_Picture_Association_film_rating_system#Rating_components + VIOLENCE, + LANGUAGE, + SUBSTANCE, + NUDITY, + SEX, + ; + + private final String jsonQName; + + private NoStrImplTagContentWarningRGroup() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + + public static Optional valueOfQName(String qName) { + return NoStrImplQName.valueOfQName(values(), qName); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagE.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagE.java new file mode 100644 index 0000000..40649bb --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagE.java @@ -0,0 +1,10 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagE implements NoStrImplEventTagArgument { + EVENT_ID, + RELAY_URL, + MARKER, + ; +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagEMarker.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagEMarker.java new file mode 100644 index 0000000..c9ddce1 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagEMarker.java @@ -0,0 +1,25 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public enum NoStrImplTagEMarker implements NoStrImplQName { + + REPLAY, ROOT, MENTION; + + private final String jsonQName; + + private NoStrImplTagEMarker() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + + public static Optional valueOfQName(String qName) { + return NoStrImplQName.valueOfQName(values(), qName); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagI.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagI.java new file mode 100644 index 0000000..76346f0 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagI.java @@ -0,0 +1,9 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagI implements NoStrImplEventTagArgument { + PLATFORM_IDENTITY, + PROOF, + ; +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagIClaim.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagIClaim.java new file mode 100644 index 0000000..8e81330 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagIClaim.java @@ -0,0 +1,46 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import java.util.Optional; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplQName; + +public enum NoStrImplTagIClaim implements NoStrImplQName { + + GITHUB, + TWITTER, + MASTODON, + TELEGRAM, + ; + + public static final String SEPERATOR = ":"; + private final String jsonQName; + + private NoStrImplTagIClaim() { + jsonQName = name().toLowerCase(); + } + + @Override + public String getQName() { + return jsonQName; + } + + public static String splitFromPlatformIdentity(String claimUri) { + int sepIdx = claimUri.indexOf(SEPERATOR); + if (sepIdx == -1) { + return claimUri; + } + if (sepIdx == claimUri.length() - 1) { + return ""; + } + return claimUri.substring(sepIdx + 1); + } + + public static Optional valueOfPlatformIdentity(String claimUri) { + for (NoStrImplTagIClaim claim : values()) { + if (claimUri.startsWith(claim.getQName())) { + return Optional.of(claim); + } + } + return Optional.empty(); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagImage.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagImage.java new file mode 100644 index 0000000..fcdeac2 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagImage.java @@ -0,0 +1,27 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagImage implements NoStrImplEventTagArgument { + URL, + DIMENSION, + ; + + public static final String DIMENSION_SEPERATOR = "x"; + + public static String createDimension(int width, int height) { + return Integer.toString(width) + DIMENSION_SEPERATOR + Integer.toString(height); + } + + public static int splitDimensionToWidth(String dimension) { + int idxSep = dimension.indexOf(DIMENSION_SEPERATOR); + String result = dimension.substring(0, idxSep); + return Integer.valueOf(result); + } + + public static int splitDimensionToHeight(String dimension) { + int idxSep = dimension.indexOf(DIMENSION_SEPERATOR); + String result = dimension.substring(idxSep + 1, dimension.length()); + return Integer.valueOf(result); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagNonce.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagNonce.java new file mode 100644 index 0000000..3b63603 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagNonce.java @@ -0,0 +1,11 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagNonce implements NoStrImplEventTagArgument { + BEARER_PROOF, + DIFFICULTY, + ; + + public static final int DEFAULT_BEARER_PROOF = 1; +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagP.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagP.java new file mode 100644 index 0000000..b689d4b --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagP.java @@ -0,0 +1,9 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagP implements NoStrImplEventTagArgument { + PUBLIC_KEY, + PET_NAME, + ; +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagSubject.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagSubject.java new file mode 100644 index 0000000..4514a54 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagSubject.java @@ -0,0 +1,43 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import java.util.Locale; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagSubject implements NoStrImplEventTagArgument { + SUBJECT, + ; + + public static final String REPLY_PREFIX = "Re:"; + public static final String REPLY_PREFIX_LOWER = REPLY_PREFIX.toLowerCase(Locale.ROOT); + public static final String REPLY_PREFIX_UPPER = REPLY_PREFIX.toUpperCase(Locale.ROOT); + + public static boolean hasReplyPrefix(String subject) { + if (subject.startsWith(REPLY_PREFIX)) { + return true; + } + if (subject.startsWith(REPLY_PREFIX_LOWER)) { + return true; + } + if (subject.startsWith(REPLY_PREFIX_UPPER)) { + return true; + } + return false; + } + + public static String prefixSubject(String subject) { + return REPLY_PREFIX + " " + subject; + } + + public static boolean isLengthValid(String subject) { + return NoStrImplTagTitle.isLengthValid(subject); + } + + public static String limitLength(String subject) { + return NoStrImplTagTitle.limitLength(subject); + } + + public static boolean hasChoppedPostfix(String subject) { + return NoStrImplTagTitle.hasChoppedPostfix(subject); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagThumb.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagThumb.java new file mode 100644 index 0000000..e5961e3 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagThumb.java @@ -0,0 +1,21 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagThumb implements NoStrImplEventTagArgument { + URL, + DIMENSION, + ; + + public static String createDimension(int width, int height) { + return NoStrImplTagImage.createDimension(width, height); + } + + public static int splitDimensionToWidth(String dimension) { + return NoStrImplTagImage.splitDimensionToWidth(dimension); + } + + public static int splitDimensionToHeight(String dimension) { + return NoStrImplTagImage.splitDimensionToHeight(dimension); + } +} diff --git a/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagTitle.java b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagTitle.java new file mode 100644 index 0000000..8675170 --- /dev/null +++ b/no2all-nostr/src/main/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagTitle.java @@ -0,0 +1,27 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTagArgument; + +public enum NoStrImplTagTitle implements NoStrImplEventTagArgument { + TITLE, + ; + + private static final int MAX_LENGTH = 64; + private static final String CHOP_INDICATOR = "..."; + + public static boolean isLengthValid(String title) { + return title.length() < MAX_LENGTH; + } + + public static String limitLength(String title) { + if (isLengthValid(title)) { + return title; + } + int idxChop = MAX_LENGTH - CHOP_INDICATOR.length(); + return title.substring(0, idxChop) + CHOP_INDICATOR; + } + + public static boolean hasChoppedPostfix(String title) { + return title.endsWith(CHOP_INDICATOR); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityTest.java new file mode 100644 index 0000000..9dc5ae3 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrIdentityTest.java @@ -0,0 +1,21 @@ +package love.distributedrebirth.no2all.nostr.model; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NoStrIdentityTest { + + @Test + public void testNotEqualKeys() { + NoStrIdentity user = new NoStrIdentity(NoStrIdentityPrivateKey.ofRandom()); + Assertions.assertNotEquals(user.getPrivateKey().getHex(), user.getPublicKey().getHex()); + } + + @Test + public void testRandom() { + NoStrIdentity user1 = new NoStrIdentity(NoStrIdentityPrivateKey.ofRandom()); + NoStrIdentity user2 = new NoStrIdentity(NoStrIdentityPrivateKey.ofRandom()); + Assertions.assertNotEquals(user1.getPrivateKey().getHex(), user2.getPrivateKey().getHex()); + Assertions.assertNotEquals(user1.getPublicKey().getHex(), user2.getPublicKey().getHex()); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrProfileMetaDataTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrProfileMetaDataTest.java new file mode 100644 index 0000000..b63c1f2 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrProfileMetaDataTest.java @@ -0,0 +1,30 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.io.StringReader; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonReader; + +public class NoStrProfileMetaDataTest { + + @Test + public void testUnknownKeys() { + JsonObject json = Json.createObjectBuilder() + .add("display_name", "junit") + .add("foo", "bar") + .build(); + NoStrProfileMetaData profileData = new NoStrProfileMetaData(json); + String jsonStr = profileData.toBible().toString(); + + try (JsonReader read = Json.createReader(new StringReader(jsonStr))) { + JsonObject jsonResult = read.readObject(); + NoStrProfileMetaData profileDataResult = new NoStrProfileMetaData(jsonResult); + Assertions.assertEquals("junit", profileDataResult.getDisplayName()); + Assertions.assertEquals("bar", jsonResult.getString("foo")); + } + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoTest.java new file mode 100644 index 0000000..2f4979e --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/NoStrRelayInfoTest.java @@ -0,0 +1,35 @@ +package love.distributedrebirth.no2all.nostr.model; + +import java.io.InputStreamReader; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import jakarta.json.Json; +import jakarta.json.JsonReader; + +public class NoStrRelayInfoTest { + + // For Example wss://relay.snort.social + //{ + // "id": "wss://relay.snort.social/", + // "name": "Snort Relay", + //etc + + @Test + public void testRead() throws Exception { + try (JsonReader read = Json.createReader(new InputStreamReader(getClass().getResourceAsStream("relay-info.json")))) { + NoStrRelayInfo relayInfo = new NoStrRelayInfo(read.readObject()); + Assertions.assertEquals("eden.nostr.land", relayInfo.getName()); + Assertions.assertEquals("1.22.6", relayInfo.getVersion()); + Assertions.assertTrue(relayInfo.getSupportedNips().contains(11)); + Assertions.assertTrue(relayInfo.getSupportedNips().contains(40)); + Assertions.assertNotNull(relayInfo.getLimitation()); + Assertions.assertEquals(10, relayInfo.getLimitation().getMaxSubscriptions()); + Assertions.assertEquals(256, relayInfo.getLimitation().getMaxSubIdLength()); + Assertions.assertTrue(relayInfo.getFeesPublication().isEmpty()); + Assertions.assertFalse(relayInfo.getFeesAdmission().isEmpty()); + Assertions.assertEquals("msats", relayInfo.getFeesAdmission().get(0).getUnit()); + } + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventSignatureTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventSignatureTest.java new file mode 100644 index 0000000..c5f78cd --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventSignatureTest.java @@ -0,0 +1,187 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.time.Duration; +import java.time.Instant; +import java.util.Optional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import jakarta.json.Json; +import jakarta.json.JsonReader; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentity; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPrivateKey; +import love.distributedrebirth.no2all.nostr.model.tag.NoStrTagNonce; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagNonce; +import love.distributedrebirth.no2all.octo.OctoBitFormat; +import love.distributedrebirth.no2all.octo.trust.OctoTrust; +import love.distributedrebirth.no2all.octo.trust.OctoTrustHash; + +public class NoStrEventSignatureTest { + + @Test + public void testReadEventVerifyOk() throws Exception { + try (JsonReader reader = Json.createReader(new InputStreamReader(getClass().getResourceAsStream("note-text-simple.json")))) { + NoStrEvent event = new NoStrEvent(reader.readObject()); + Assertions.assertTrue(NoStrEventSignature.verify(event)); + } + } + + @Test + public void testReadEventVerifyFail() throws Exception { + try (JsonReader reader = Json.createReader(new InputStreamReader(getClass().getResourceAsStream("note-text-sign-error.json")))) { + NoStrEvent event = new NoStrEvent(reader.readObject()); + Assertions.assertFalse(NoStrEventSignature.verify(event)); + } + } + + @Test + public void testMining() throws Exception { + Duration maxMining = Duration.ofSeconds(360); + int maxDifficulty = (OctoTrust.KEY_LENGTH * 8) / 2; // is 128 thus 50% of bits are zero + NoStrIdentity nid = new NoStrIdentity(NoStrIdentityPrivateKey.ofRandom()); + try (JsonReader reader = Json.createReader(new InputStreamReader(getClass().getResourceAsStream("note-text-simple.json")))) { + NoStrEvent event = new NoStrEvent(reader.readObject()); + Assertions.assertTrue(NoStrEventSignature.verify(event)); + int testOffsetDifficulty = 10; // +12 from note-text-simple.json + + // use test public key and copy others + NoStrEventPayload payload = new NoStrEventPayload(nid.getPublicKey(), event.getPayload().getCreatedAt(), event.getPayload().getKind(), event.getPayload().getTags(), event.getPayload().getContent()); + Optional nonceTagOpt = payload.findFirstByQName(NoStrImplEventTag.NONCE); + if (nonceTagOpt.isEmpty()) { + return; // no mining requested + } + // replace X NoStrEventTag impl or NoStrEventTagCustom or NoStrTagNonce with local tag for setter access + NoStrTagNonce nonceTag = new NoStrTagNonce(Integer.parseInt(nonceTagOpt.get().getMetaArgument(NoStrImplTagNonce.DIFFICULTY).get()) + testOffsetDifficulty); + int nonceTagIdx = payload.getTags().indexOf(nonceTagOpt.get()); + payload.getTags().set(nonceTagIdx, nonceTag); + + int targetDifficulty = nonceTag.getDifficulty(); + if (targetDifficulty > maxDifficulty) { + throw new IllegalArgumentException("Target difficulty can't be larger than " + maxDifficulty); + } + int mineCnt = 0; + byte[] eventId = null; + int leadingZeroBits = 0; + byte[] nowStr = Long.toString(payload.getCreatedAt().getEpochSecond()).getBytes(StandardCharsets.UTF_8); + byte[] payloadStr = NoStrEventSignature.generateEventIdJsonString(payload).getBytes(StandardCharsets.UTF_8); + int createdAtLength = nowStr.length; + int createdAtIdx = indexOf(payloadStr, nowStr, indexOf(payloadStr, new byte[]{'\"'}, 5)); + int nonceProofIdx = indexOf(payloadStr, "\"nonce\"".getBytes(StandardCharsets.UTF_8), 0) + 9;// ["nonce","7539","12"]], + int nonceProofLength = Integer.toString(nonceTag.getBearerProof()).getBytes(StandardCharsets.UTF_8).length; + long createdAtTime = System.currentTimeMillis(); + long maxMiningTime = createdAtTime + maxMining.toMillis(); + MessageDigest sha256 = OctoTrustHash.sha256Algorithm(); + + while (leadingZeroBits < targetDifficulty) { + mineCnt++; + createdAtTime = System.currentTimeMillis(); + if (createdAtTime > maxMiningTime) { + throw new IllegalStateException("Mining resource limit exceeded"); + } + long createdAtTimeSecs = createdAtTime / 1000; + int createdAtTimeDigits = numDigits(createdAtTimeSecs); + if (createdAtLength != createdAtTimeDigits) { + createdAtLength = createdAtTimeDigits; + payloadStr = growBySplit(payloadStr, createdAtIdx); + } + writeDigits(createdAtTimeSecs, createdAtTimeDigits, payloadStr, createdAtIdx); + + int nonceProofDigits = numDigits(mineCnt); + if (nonceProofLength != nonceProofDigits) { + nonceProofLength = nonceProofDigits; + payloadStr = growBySplit(payloadStr, nonceProofIdx); + } + writeDigits(mineCnt, nonceProofDigits, payloadStr, nonceProofIdx); + + eventId = sha256.digest(payloadStr); + leadingZeroBits = 0; + for (int i = 0; i < eventId.length; i++) { + int step = eventId[i] & 0xFF; // force unsigned + if (step == 0) { + leadingZeroBits += 8; + continue; + } + leadingZeroBits += Integer.numberOfLeadingZeros(step) - 24; + break; + } + } + payload.setCreatedAt(Instant.ofEpochMilli(createdAtTime)); + nonceTag.setBearerProof(mineCnt); + + NoStrEvent eventMined = NoStrEventSignature.sign(payload, nid.getPrivateKey(), eventId); + + System.out.println("idOrg=" + event.getId().getHex()); + System.out.println("idHex=" + eventMined.getId().getHex()); + System.out.println("idStr=" + NoStrEventSignature.generateEventIdJsonString(payload)); + System.out.println("leadZero=" + leadingZeroBits); + System.out.println("mineCnt=" + mineCnt); + System.out.println("idStrHex=" + OctoBitFormat.HEX.fromBytes(NoStrEventSignature.generateEventIdJsonString(payload).getBytes(StandardCharsets.UTF_8))); + System.out.println("idStrArr=" + OctoBitFormat.HEX.fromBytes(payloadStr)); + + Assertions.assertTrue(NoStrEventSignature.verify(eventMined)); + Assertions.assertEquals("" + mineCnt, eventMined.getPayload().findFirstByQName("nonce").get().getMetaArgument(0).get()); + Assertions.assertEquals("" + targetDifficulty, eventMined.getPayload().findFirstByQName("nonce").get().getMetaArgument(1).get()); + } + } + + public byte[] growBySplit(byte[] src, int splitIdx) { + byte[] result = new byte[src.length + 1]; + System.arraycopy(src, 0, result, 0, splitIdx); + System.arraycopy(src, splitIdx + 1, result, splitIdx + 2, src.length - splitIdx - 1); + return result; + } + + public void writeDigits(long value, int digits, byte[] target, int off) { + int i = digits - 1; + while (value > 0) { + target[i+off] = (byte) ((value % 10) + '0'); + value /= 10; + i--; + } + } + + public int numDigits(long n) { + return (int) Math.log10(n) + 1; + } + + public int indexOf(byte[] source, byte[] target, int fromIndex) { + return indexOf(source, 0, source.length, target, 0, target.length, fromIndex); + } + + public int indexOf(byte[] source, int sourceOffset, int sourceCount, byte[] target, int targetOffset, int targetCount, int fromIndex) { + if (fromIndex >= sourceCount) { + return (targetCount == 0 ? sourceCount : -1); + } + if (fromIndex < 0) { + fromIndex = 0; + } + if (targetCount == 0) { + return fromIndex; + } + byte first = target[targetOffset]; + int max = sourceOffset + (sourceCount - targetCount); + for (int i = sourceOffset + fromIndex; i <= max; i++) { + if (source[i] != first) { // search first + while (++i <= max && source[i] != first) { + ; + } + } + if (i <= max) { // search left over + int j = i + 1; + int end = j + targetCount - 1; + for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++) { + ; + } + if (j == end) { + return i - sourceOffset; + } + } + } + return -1; + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagTest.java new file mode 100644 index 0000000..f700f43 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTagTest.java @@ -0,0 +1,21 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.util.Optional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public class NoStrEventTagTest { + + @Test + public void testArgumentResult() { + NoStrEventTag tag = NoStrEventTag.valueOfArgs(NoStrImplEventTag.CLIENT, "0", "1"); + Assertions.assertEquals(2, tag.getMetaArguments().length); + Assertions.assertEquals(Optional.empty(), tag.getMetaArgument(-1)); + Assertions.assertEquals(Optional.empty(), tag.getMetaArgument(2)); + Assertions.assertTrue(tag.getMetaArgument(0).isPresent()); + Assertions.assertEquals("1", tag.getMetaArgument(1).get()); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTest.java new file mode 100644 index 0000000..a534ade --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/event/NoStrEventTest.java @@ -0,0 +1,64 @@ +package love.distributedrebirth.no2all.nostr.model.event; + +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import jakarta.json.Json; +import jakarta.json.JsonReader; +import jakarta.json.JsonWriter; +import jakarta.json.JsonWriterFactory; +import jakarta.json.stream.JsonGenerator; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventTag; + +public class NoStrEventTest { + + @Test + public void testReadEvent() throws Exception { + try (JsonReader reader = Json.createReader(new InputStreamReader(getClass().getResourceAsStream("note-text-sign-error.json")))) { + NoStrEvent event = new NoStrEvent(reader.readObject()); + NoStrEventPayload payload = event.getPayload(); + Assertions.assertEquals("000c5b8f354d200abd7dc0bc81bd35153aebe72824c23e834db5feb83591c20d", event.getId().getHex()); + Assertions.assertEquals(1, payload.getKind().getNumber()); + Optional clientTag = payload.findFirstByQName(NoStrImplEventTag.CLIENT); + Assertions.assertTrue(clientTag.isPresent()); + Assertions.assertEquals("more-speech", clientTag.get().getMetaArgument(0).get().split(" ")[0]); + Assertions.assertEquals(13, payload.findByQName(NoStrImplEventTag.P).size()); + Optional eTag = payload.findFirstByQName(NoStrImplEventTag.E); + Assertions.assertTrue(eTag.isPresent()); + Assertions.assertEquals("d54e918e87b55253315df49cc44e60ca551b9124c2a3dc1b4d68c57e13ba8422", eTag.get().getMetaArgument(0).get()); + Assertions.assertEquals("reply", eTag.get().getMetaArgument(2).get()); + } + } + + @Test + public void testReadEventPritty() throws Exception { + Map config = new HashMap<>(); + config.put(JsonGenerator.PRETTY_PRINTING, true); + JsonWriterFactory prittyFactory = Json.createWriterFactory(config); + try (JsonReader reader = Json.createReader(new InputStreamReader(getClass().getResourceAsStream("note-text-sign-error.json")))) { + NoStrEvent event = new NoStrEvent(reader.readObject()); + StringWriter jsonStr = new StringWriter(); + try (JsonWriter writer = prittyFactory.createWriter(jsonStr)) { + writer.write(event.toBible()); + } + System.out.println(jsonStr.getBuffer()); + } + } + + @Test + public void testEmptyTags() throws Exception { + try (JsonReader reader = Json.createReader(new InputStreamReader(getClass().getResourceAsStream("note-empty-tags.json")))) { + NoStrEvent event = new NoStrEvent(reader.readObject()); + NoStrEventPayload payload = event.getPayload(); + Assertions.assertEquals("fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", event.getId().getHex()); + Assertions.assertEquals(1, payload.getKind().getNumber()); + Assertions.assertTrue(payload.getTags().isEmpty()); + } + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEventTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEventTest.java new file mode 100644 index 0000000..25dfc6d --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkEventTest.java @@ -0,0 +1,28 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventId; + +public class NoStrShareLinkEventTest { + + @Test + public void testToObject() { + NoStrEventId key1 = NoStrShareLinkEvent.ofB32("nevent1qqsqqrzm3u656gq2h47up0yph5632whtuu5zfs37sdxmtl4cxkguyrgvg94ye").getEventId(); + Assertions.assertEquals("000c5b8f354d200abd7dc0bc81bd35153aebe72824c23e834db5feb83591c20d", key1.getHex()); + } + + @Test + public void testToString() { + NoStrEventId eventId1 = NoStrEventId.ofHex("000c5b8f354d200abd7dc0bc81bd35153aebe72824c23e834db5feb83591c20d"); + NoStrShareLinkEvent linkEvent1 = new NoStrShareLinkEvent(eventId1); + NoStrShareLinkNote linkNote1 = new NoStrShareLinkNote(eventId1); + Assertions.assertEquals("nevent1qqsqqrzm3u656gq2h47up0yph5632whtuu5zfs37sdxmtl4cxkguyrgvg94ye", linkEvent1.toShareLink()); + Assertions.assertEquals("note1qqx9hre4f5sq40tacz7gr0f4z5awheegynpraq6dkhltsdv3cgxszha4k8", linkNote1.toShareLink()); + + NoStrEventId eventId2 = NoStrEventId.ofHex("d94a3f4dd87b9a3b0bed183b32e916fa29c8020107845d1752d72697fe5309a5"); + NoStrShareLinkNote link2 = new NoStrShareLinkNote(eventId2); + Assertions.assertEquals("note1m99r7nwc0wdrkzldrqan96gklg5usqspq7z9696j6unf0ljnpxjspqfw99", link2.toShareLink()); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKeyTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKeyTest.java new file mode 100644 index 0000000..bd13e87 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPrivateKeyTest.java @@ -0,0 +1,25 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPrivateKey; + +public class NoStrShareLinkPrivateKeyTest { + + @Test + public void testToObject() { + NoStrIdentityPrivateKey key1 = NoStrShareLinkPrivateKey.ofB32("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99").getPrivateKey(); + Assertions.assertEquals("9571a568a42b9e05646a349c783159b906b498119390df9a5a02667155128028", key1.getHex()); + + NoStrIdentityPrivateKey key2 = NoStrShareLinkPrivateKey.ofB32("nsec1vl029mgpspedva04g90vltkh6fvh240zqtv9k0t9af8935ke9laqsnlfe5").getPrivateKey(); + Assertions.assertEquals("67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa", key2.getHex()); + } + + @Test + public void testToString() { + NoStrIdentityPrivateKey key1 = NoStrIdentityPrivateKey.ofHex("9571a568a42b9e05646a349c783159b906b498119390df9a5a02667155128028"); + NoStrShareLinkPrivateKey keyLink1 = new NoStrShareLinkPrivateKey(key1); + Assertions.assertEquals("nsec1j4c6269y9w0q2er2xjw8sv2ehyrtfxq3jwgdlxj6qfn8z4gjsq5qfvfk99", keyLink1.toShareLink()); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkProfileTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkProfileTest.java new file mode 100644 index 0000000..2529aab --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkProfileTest.java @@ -0,0 +1,54 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import java.util.Base64; +import java.util.List; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class NoStrShareLinkProfileTest { + + @Test + public void testToObject() { + NoStrShareLinkProfile profile = NoStrShareLinkProfile.ofB32("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p"); + Assertions.assertEquals("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d", profile.getPublicKey().getHex()); + Assertions.assertEquals(2, profile.getRelays().size()); + Assertions.assertEquals("wss://r.x.com", profile.getRelays().get(0)); + Assertions.assertEquals("wss://djbas.sadkb.com", profile.getRelays().get(1)); + } + + @Test + public void testToString() { + List relays = List.of("wss://r.x.com", "wss://djbas.sadkb.com"); + NoStrShareLinkProfile profile = new NoStrShareLinkProfile(NoStrIdentityPublicKey.ofHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"), relays); + Assertions.assertEquals("nprofile1qqsrhuxx8l9ex335q7he0f09aej04zpazpl0ne2cgukyawd24mayt8gpp4mhxue69uhhytnc9e3k7mgpz4mhxue69uhkg6nzv9ejuumpv34kytnrdaksjlyr9p", profile.toShareLink()); + } + + @Test + public void testRelayUrls() { + //https://en.wikipedia.org/wiki/Internationalized_domain_name + List relays = List.of("wss://仙上主天.中国", "wss://ουτοπία.δπθ.gr", "wss://ԝԝԝ.facebook.com", "wss://😉.tld"); + NoStrShareLinkProfile profile = new NoStrShareLinkProfile(NoStrIdentityPublicKey.ofHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d"), relays); + + byte[] data = profile.getTapeDeck().chain.toByteArray(); + String b64 = Base64.getEncoder().encodeToString(data); + System.out.println("hex_h16="+OctoBitFormat.HEX.fromBytes(data)); + System.out.println("hex_ccp="+OctoBitFormat.HEX_CCP.fromBytes(data)); + System.out.println("hex_lrp="+OctoBitFormat.HEX_DIPAVALI.fromBytes(data)); + System.out.println("b64="+b64); + System.out.println("b32="+profile.toShareLink()); + System.out.println("----------"); + byte[] linkData = profile.toShareLink().getBytes(); + System.out.println("ccp="+OctoBitFormat.HEX_CCP.fromBytes(linkData)); + System.out.println("c6p="+Base64.getEncoder().encodeToString(linkData)); + + NoStrShareLinkProfile profileWire = NoStrShareLinkProfile.ofB32(profile.toShareLink()); + Assertions.assertEquals(relays.get(0), profileWire.getRelays().get(0)); + Assertions.assertEquals(relays.get(1), profileWire.getRelays().get(1)); + Assertions.assertEquals(relays.get(2), profileWire.getRelays().get(2)); + Assertions.assertEquals(relays.get(3), profileWire.getRelays().get(3)); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPublicKeyTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPublicKeyTest.java new file mode 100644 index 0000000..ba87101 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/share/NoStrShareLinkPublicKeyTest.java @@ -0,0 +1,24 @@ +package love.distributedrebirth.no2all.nostr.model.share; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPublicKey; + +public class NoStrShareLinkPublicKeyTest { + + @Test + public void testToObject() { + NoStrIdentityPublicKey key1 = NoStrShareLinkPublicKey.ofB32("npub10elfcs4fr0l0r8af98jlmgdh9c8tcxjvz9qkw038js35mp4dma8qzvjptg").getPublicKey(); + Assertions.assertEquals("7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e", key1.getHex()); + } + + @Test + public void testToString() { + NoStrShareLinkPublicKey key1 = new NoStrShareLinkPublicKey(NoStrIdentityPublicKey.ofHex("aa4fc8665f5696e33db7e1a572e3b0f5b3d615837b0f362dcb1c8068b098c7b4")); + Assertions.assertEquals("npub14f8usejl26twx0dhuxjh9cas7keav9vr0v8nvtwtrjqx3vycc76qqh9nsy", key1.toShareLink()); + + NoStrShareLinkPublicKey key2 = new NoStrShareLinkPublicKey(NoStrIdentityPublicKey.ofHex("3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")); + Assertions.assertEquals("npub180cvv07tjdrrgpa0j7j7tmnyl2yr6yr7l8j4s3evf6u64th6gkwsyjh6w6", key2.toShareLink()); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagETest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagETest.java new file mode 100644 index 0000000..3a59c71 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/model/tag/NoStrTagETest.java @@ -0,0 +1,22 @@ +package love.distributedrebirth.no2all.nostr.model.tag; + +import java.util.Optional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventId; +import love.distributedrebirth.no2all.nostr.nip.tag.NoStrImplTagEMarker; + +public class NoStrTagETest { + + @Test + public void testOptionalRelayUrl() { + NoStrTagE tag1 = new NoStrTagE(new String[4]); + Assertions.assertTrue(tag1.getRelayUrl().isEmpty()); + NoStrEventId eventId = NoStrEventId.ofHex("c99ba1058a872e10343820357506f546410fe5d1318eb0fbb6f352d823fa83d4"); + NoStrImplTagEMarker eventMarker = NoStrImplTagEMarker.ROOT; + NoStrTagE tag2 = new NoStrTagE(eventId, Optional.empty(), Optional.of(eventMarker)); + Assertions.assertEquals("[\"e\",\"c99ba1058a872e10343820357506f546410fe5d1318eb0fbb6f352d823fa83d4\",\"\",\"root\"]", tag2.toBible().toString()); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindRangeTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindRangeTest.java new file mode 100644 index 0000000..271fb9e --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindRangeTest.java @@ -0,0 +1,40 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NoStrImplEventKindRangeTest { + + @Test + public void testValueOfKindValid() { + Assertions.assertEquals(NoStrImplEventKindRange.CHAT, NoStrImplEventKindRange.valueOfKind(0)); + Assertions.assertEquals(NoStrImplEventKindRange.CHAT, NoStrImplEventKindRange.valueOfKind(1)); + Assertions.assertEquals(NoStrImplEventKindRange.CHAT, NoStrImplEventKindRange.valueOfKind(999)); + + Assertions.assertEquals(NoStrImplEventKindRange.REGULAR, NoStrImplEventKindRange.valueOfKind(1000)); + Assertions.assertEquals(NoStrImplEventKindRange.REGULAR, NoStrImplEventKindRange.valueOfKind(1001)); + Assertions.assertEquals(NoStrImplEventKindRange.REGULAR, NoStrImplEventKindRange.valueOfKind(9999)); + + Assertions.assertEquals(NoStrImplEventKindRange.REGULAR_REPL, NoStrImplEventKindRange.valueOfKind(10000)); + Assertions.assertEquals(NoStrImplEventKindRange.REGULAR_REPL, NoStrImplEventKindRange.valueOfKind(10001)); + Assertions.assertEquals(NoStrImplEventKindRange.REGULAR_REPL, NoStrImplEventKindRange.valueOfKind(19999)); + + Assertions.assertEquals(NoStrImplEventKindRange.EPHEMERAL, NoStrImplEventKindRange.valueOfKind(20000)); + Assertions.assertEquals(NoStrImplEventKindRange.EPHEMERAL, NoStrImplEventKindRange.valueOfKind(20001)); + Assertions.assertEquals(NoStrImplEventKindRange.EPHEMERAL, NoStrImplEventKindRange.valueOfKind(29999)); + + Assertions.assertEquals(NoStrImplEventKindRange.PARAM_REPL, NoStrImplEventKindRange.valueOfKind(30000)); + Assertions.assertEquals(NoStrImplEventKindRange.PARAM_REPL, NoStrImplEventKindRange.valueOfKind(30001)); + Assertions.assertEquals(NoStrImplEventKindRange.PARAM_REPL, NoStrImplEventKindRange.valueOfKind(39999)); + } + + @Test + public void testValueOfKindUnknown() { + Assertions.assertEquals(NoStrImplEventKindRange.UNKNOWN, NoStrImplEventKindRange.valueOfKind(40000)); + Assertions.assertEquals(NoStrImplEventKindRange.UNKNOWN, NoStrImplEventKindRange.valueOfKind(40001)); + Assertions.assertEquals(NoStrImplEventKindRange.UNKNOWN, NoStrImplEventKindRange.valueOfKind(49999)); + + Assertions.assertEquals(NoStrImplEventKindRange.UNKNOWN, NoStrImplEventKindRange.valueOfKind(-1)); + Assertions.assertEquals(NoStrImplEventKindRange.UNKNOWN, NoStrImplEventKindRange.valueOfKind(-2)); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindTest.java new file mode 100644 index 0000000..b862d6c --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplEventKindTest.java @@ -0,0 +1,23 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NoStrImplEventKindTest { + + @Test + public void testValueOfKindValid() { + Assertions.assertEquals(NoStrImplEventKind.METADATA, NoStrImplEventKind.valueOfKind(0)); + Assertions.assertEquals(NoStrImplEventKind.TEXT_NOTE, NoStrImplEventKind.valueOfKind(1)); + Assertions.assertEquals(NoStrImplEventKind.CHANNEL_SEND_MSG, NoStrImplEventKind.valueOfKind(42)); + Assertions.assertEquals(NoStrImplEventKind.LONG_FORM_CONTENT, NoStrImplEventKind.valueOfKind(30023)); + } + + @Test + public void testValueOfKindFeature() { + Assertions.assertEquals(NoStrImpl.NIP_1_BASE_PROTOCOL, NoStrImplEventKind.valueOfKind(0).getNip()); + Assertions.assertEquals(NoStrImpl.NIP_1_BASE_PROTOCOL, NoStrImplEventKind.valueOfKind(1).getNip()); + Assertions.assertEquals(NoStrImpl.NIP_28_PUBLIC_CHAT, NoStrImplEventKind.valueOfKind(42).getNip()); + Assertions.assertEquals(NoStrImpl.NIP_23_LONG_FORM_CONTENT, NoStrImplEventKind.valueOfKind(30023).getNip()); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageOkReasonTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageOkReasonTest.java new file mode 100644 index 0000000..61abf76 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplMessageOkReasonTest.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NoStrImplMessageOkReasonTest { + + @Test + public void testValueOfPrefixDefault() { + Assertions.assertEquals(NoStrImplMessageOkReason.DEFAULT, NoStrImplMessageOkReason.valueOfPrefix("")); + Assertions.assertEquals(NoStrImplMessageOkReason.DEFAULT, NoStrImplMessageOkReason.valueOfPrefix("foo")); + Assertions.assertEquals(NoStrImplMessageOkReason.DEFAULT, NoStrImplMessageOkReason.valueOfPrefix("bar")); + } + + @Test + public void testValueOfPrefixValid() { + Assertions.assertEquals(NoStrImplMessageOkReason.BLOCKED, NoStrImplMessageOkReason.valueOfPrefix("blocked: junit")); + Assertions.assertEquals(NoStrImplMessageOkReason.INVALID, NoStrImplMessageOkReason.valueOfPrefix("invalid: junit")); + Assertions.assertEquals(NoStrImplMessageOkReason.POW, NoStrImplMessageOkReason.valueOfPrefix("pow: junit")); + Assertions.assertEquals(NoStrImplMessageOkReason.RATE_LIMITED, NoStrImplMessageOkReason.valueOfPrefix("rate-limited: junit")); + Assertions.assertEquals(NoStrImplMessageOkReason.ERROR, NoStrImplMessageOkReason.valueOfPrefix("error: junit")); + } + + @Test + public void testFormatDefault() { + Assertions.assertEquals("junit", NoStrImplMessageOkReason.DEFAULT.format("junit")); + Assertions.assertEquals("error: junit", NoStrImplMessageOkReason.DEFAULT.format("error: junit")); + } + + @Test + public void testFormatError() { + Assertions.assertEquals("error: junit", NoStrImplMessageOkReason.ERROR.format("junit")); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplTest.java new file mode 100644 index 0000000..627b35b --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/NoStrImplTest.java @@ -0,0 +1,30 @@ +package love.distributedrebirth.no2all.nostr.nip; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NoStrImplTest { + + @Test + public void testNumber() { + Assertions.assertEquals(0, NoStrImpl.NIP_0_UNKNOWN.getNumber()); + Assertions.assertEquals(9, NoStrImpl.NIP_9_EVENT_DELETION.getNumber()); + Assertions.assertEquals(10, NoStrImpl.NIP_10_CORRECT_BASE_TAGS.getNumber()); + Assertions.assertEquals(33, NoStrImpl.NIP_33_PARA_REPLACEABLE_EVENTS.getNumber()); + Assertions.assertEquals(94, NoStrImpl.NIP_94_FILE_METADATA.getNumber()); + } + + @Test + public void testValueOfNIP() { + Assertions.assertEquals(NoStrImpl.NIP_0_UNKNOWN, NoStrImpl.valueOfNIP(0)); + Assertions.assertEquals(NoStrImpl.NIP_1_BASE_PROTOCOL, NoStrImpl.valueOfNIP(1)); + } + + @Test + public void testValueOfNIPValid() { + Assertions.assertTrue(NoStrImpl.valueOfNIPValid(0)); + Assertions.assertTrue(NoStrImpl.valueOfNIPValid(13)); + Assertions.assertFalse(NoStrImpl.valueOfNIPValid(-1)); + Assertions.assertFalse(NoStrImpl.valueOfNIPValid(666)); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagATest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagATest.java new file mode 100644 index 0000000..05ff542 --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagATest.java @@ -0,0 +1,15 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NoStrImplTagATest { + + @Test + public void testSplitting() { + String test = "8::"; + Assertions.assertEquals(8, NoStrImplTagA.splitToKind(test)); + Assertions.assertEquals("", NoStrImplTagA.splitToPublicKey(test)); + Assertions.assertEquals("", NoStrImplTagA.splitToParameter(test)); + } +} diff --git a/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagIClaimTest.java b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagIClaimTest.java new file mode 100644 index 0000000..846c92a --- /dev/null +++ b/no2all-nostr/src/test/java/love/distributedrebirth/no2all/nostr/nip/tag/NoStrImplTagIClaimTest.java @@ -0,0 +1,39 @@ +package love.distributedrebirth.no2all.nostr.nip.tag; + +import java.util.Optional; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class NoStrImplTagIClaimTest { + + private static final String TEST_FOOBAR = "foobar:junit"; + private static final String TEST_GITHUB = "github:semisol"; + private static final String TEST_TWITTER = "twitter:semisol_public"; + private static final String TEST_MASTODON = "mastodon:bitcoinhackers.org/@semisol"; + private static final String TEST_TELEGRAM = "telegram:1087295469"; + + @Test + public void testValueOfPrefix() { + Assertions.assertEquals(Optional.empty(), NoStrImplTagIClaim.valueOfPlatformIdentity(TEST_FOOBAR)); + Assertions.assertEquals(NoStrImplTagIClaim.GITHUB, NoStrImplTagIClaim.valueOfPlatformIdentity(TEST_GITHUB).get()); + Assertions.assertEquals(NoStrImplTagIClaim.TWITTER, NoStrImplTagIClaim.valueOfPlatformIdentity(TEST_TWITTER).get()); + Assertions.assertEquals(NoStrImplTagIClaim.MASTODON, NoStrImplTagIClaim.valueOfPlatformIdentity(TEST_MASTODON).get()); + Assertions.assertEquals(NoStrImplTagIClaim.TELEGRAM, NoStrImplTagIClaim.valueOfPlatformIdentity(TEST_TELEGRAM).get()); + } + + @Test + public void testSplitToIdentity() { + Assertions.assertEquals("junit", NoStrImplTagIClaim.splitFromPlatformIdentity(TEST_FOOBAR)); + Assertions.assertEquals("semisol", NoStrImplTagIClaim.splitFromPlatformIdentity(TEST_GITHUB)); + } + + @Test + public void testSplitToIdentityInvalid() { + Assertions.assertEquals("", NoStrImplTagIClaim.splitFromPlatformIdentity("")); + Assertions.assertEquals("123", NoStrImplTagIClaim.splitFromPlatformIdentity("123")); + Assertions.assertEquals("", NoStrImplTagIClaim.splitFromPlatformIdentity("123:")); + Assertions.assertEquals("456", NoStrImplTagIClaim.splitFromPlatformIdentity("123:456")); + + } +} diff --git a/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-empty-tags.json b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-empty-tags.json new file mode 100644 index 0000000..c1554ee --- /dev/null +++ b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-empty-tags.json @@ -0,0 +1,9 @@ +{ + "content": "直んないわ。まあええか", + "created_at": 1686199583, + "id": "fc7f200c5bed175702bd06c7ca5dba90d3497e827350b42fc99c3a4fa276a712", + "kind": 1, + "pubkey": "8c59239319637f97e007dad0d681e65ce35b1ace333b629e2d33f9465c132608", + "sig": "9584afd231c52fcbcec6ce668a2cc4b6dc9b4d9da20510dcb9005c6844679b4844edb7a2e1e0591958b0295241567c774dbf7d39a73932877542de1a5f963f4b", + "tags": [] +} \ No newline at end of file diff --git a/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-text-sign-error.json b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-text-sign-error.json new file mode 100644 index 0000000..d54ff10 --- /dev/null +++ b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-text-sign-error.json @@ -0,0 +1,81 @@ +{ + "content": "Arhythmia is an issue you should pay attention to...dead. #[9]​ #[10]​ #[11]​ #[12]​ #[13]​ #[14]", + "created_at": 1685743927, + "id": "000c5b8f354d200abd7dc0bc81bd35153aebe72824c23e834db5feb83591c20d", + "kind": 1, + "pubkey": "2ef93f01cd2493e04235a6b87b10d3c4a74e2a7eb7c3caf168268f6af73314b5", + "sig": "5aeeba7e11c4e32c42c219ff7286319e297ff997e2f683c588be5b78d946c6ebb8124e192bc0e9ace580f25b906ebaecb70adee1329a4b53ed4946fbac521076", + "tags": [ + [ + "e", + "d54e918e87b55253315df49cc44e60ca551b9124c2a3dc1b4d68c57e13ba8422", + "", + "reply" + ], + [ + "p", + "df0aed92dff00d4ffeda2f4cd88d967dc576b5549400b92afb98a419aa06e6f5" + ], + [ + "client", + "more-speech - 2023-05-23T09:23" + ], + [ + "p", + "52b4a076bcbbbdc3a1aefa3735816cf74993b1b8db202b01c883c58be7fad8bd" + ], + [ + "p", + "8fb140b4e8ddef97ce4b821d247278a1a4353362623f64021484b372f948000c" + ], + [ + "p", + "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" + ], + [ + "p", + "3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24" + ], + [ + "p", + "c48e29f04b482cc01ca1f9ef8c86ef8318c059e0e9353235162f080f26e14c11" + ], + [ + "p", + "bd1e19980e2c91e6dc657e92c25762ca882eb9272d2579e221f037f93788de91" + ], + [ + "p", + "3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24" + ], + [ + "p", + "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245" + ], + [ + "p", + "52b4a076bcbbbdc3a1aefa3735816cf74993b1b8db202b01c883c58be7fad8bd" + ], + [ + "p", + "8fb140b4e8ddef97ce4b821d247278a1a4353362623f64021484b372f948000c" + ], + [ + "p", + "c48e29f04b482cc01ca1f9ef8c86ef8318c059e0e9353235162f080f26e14c11" + ], + [ + "p", + "bd1e19980e2c91e6dc657e92c25762ca882eb9272d2579e221f037f93788de91" + ], + [ + "nonce", + "800", + "12" + ] + ], + "seenOn": [ + "wss://relay.damus.io/", + "wss://nostr.wine/" + ] +} \ No newline at end of file diff --git a/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-text-simple.json b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-text-simple.json new file mode 100644 index 0000000..a30ad92 --- /dev/null +++ b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/event/note-text-simple.json @@ -0,0 +1,51 @@ +{ + "content": "Supreme Court Justice Louis Brandeis advised, in his famous Whitney v. California opinion in 1927, \"If there be time to expose through discussion the falsehood and fallacies, to avert the evil by the processes of education, the remedy to be applied is more speech, not enforced silence.\"\n\nFrom: elidy<-mazin at 06/04/23 12:52:05 on wss://relay.damus.io\nCC: #[4]\nCC: #[5]\n>---------------\n>I wonder what’s the story behind the name more-speech #[6]", + "created_at": 1685973097, + "id": "00051b7a0388402920bb577c6755ea8fc154861490834937f5741d6c6f596204", + "kind": 1, + "pubkey": "2ef93f01cd2493e04235a6b87b10d3c4a74e2a7eb7c3caf168268f6af73314b5", + "sig": "a219b7d632129b8bbba94ed1d81aa8bef9297be9bd4d59bae826a865d47c3973dccda635665ae3cec18d8003aaa75f87326a8df3efbc9c7ac35617628829c919", + "tags": [ + [ + "e", + "56b37f3968849924859f91ed3d049587cb723745aaed6502a6a1bf1e3a671696", + "", + "root" + ], + [ + "e", + "8dad1493450b4c26d365bdc0946a7e7730efaa41f13a544b16664d33353f1425", + "", + "reply" + ], + [ + "p", + "efa6abd09142caf23dfb70ed3b9bd549042901caa66f686259a1cc55a4970369" + ], + [ + "client", + "more-speech - 2023-05-23T09:23" + ], + [ + "p", + "6e468422dfb74a5738702a8823b9b28168abab8655faacb6853cd0ee15deee93" + ], + [ + "p", + "2ef93f01cd2493e04235a6b87b10d3c4a74e2a7eb7c3caf168268f6af73314b5" + ], + [ + "p", + "2ef93f01cd2493e04235a6b87b10d3c4a74e2a7eb7c3caf168268f6af73314b5" + ], + [ + "nonce", + "3405", + "12" + ] + ], + "seenOn": [ + "wss://relay.damus.io/", + "wss://nostr.wine/" + ] +} \ No newline at end of file diff --git a/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/relay-info.json b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/relay-info.json new file mode 100644 index 0000000..f5b56d9 --- /dev/null +++ b/no2all-nostr/src/test/resources/love/distributedrebirth/no2all/nostr/model/relay-info.json @@ -0,0 +1,22 @@ +{"name":"eden.nostr.land", +"description":"Eden Nostr Land - Toronto 1-01", +"pubkey":"00000000827ffaa94bfea288c3dfce4422c794fbb96625b6b31e9049f729d700", +"contact":"me@ricardocabral.io", +"supported_nips":[1,2,4,9,11,12,15,16,20,22,26,28,33,40], +"supported_nip_extensions":["11a"], +"software":"git+https://github.com/Cameri/nostream.git", +"version":"1.22.6", +"limitation":{"max_message_length":1048576, + "max_subscriptions":10, + "max_filters":2500, + "max_limit":5000, + "max_subid_length":256, + "min_prefix":4, + "max_event_tags":2500, + "max_content_length":65536, + "min_pow_difficulty":0, + "auth_required":false, + "payment_required":true}, +"payments_url":"https://eden.nostr.land/invoices", +"fees":{"admission":[{"amount":5000000,"unit":"msats"}], + "publication":[]}} \ No newline at end of file diff --git a/no2all-octo/pom.xml b/no2all-octo/pom.xml new file mode 100644 index 0000000..f022607 --- /dev/null +++ b/no2all-octo/pom.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-octo + No2All-Octo + + + org.bouncycastle + bcprov-jdk18on + + + \ No newline at end of file diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32.java new file mode 100644 index 0000000..86cbb3b --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32.java @@ -0,0 +1,194 @@ +package love.distributedrebirth.no2all.octo; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; + +public final class OctoBech32 { + + private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; + private static final byte[] CHARSET_REV = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, 25, 9, 8, 23, + -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, -1, 29, -1, 24, 13, + 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 }; + + public static OctoBech32String toBech32(String hrp, byte[] hexKey) { + byte[] data = convertBits(hexKey, 8, 5, true); + return encode(new OctoBech32Bucket(OctoBech32Variant.BECH32, hrp, data)); + } + + public static OctoBech32Bucket fromBech32(OctoBech32String link) { + OctoBech32Bucket shareB32 = decode(link); + byte[] data = shareB32.getData(); + data = convertBits(data, 5, 8, true); + data = Arrays.copyOfRange(data, 0, data.length - 1); + return new OctoBech32Bucket(shareB32.getVariant(), shareB32.getHrp(), data); + } + + public static OctoBech32String encode(OctoBech32Bucket shareB32) { + if (shareB32.getHrp().length() < 1) { + throw new IllegalArgumentException("Human-readable part is too short"); + } + String hrp = shareB32.getHrp().toLowerCase(Locale.ROOT); + byte[] values = shareB32.getData(); + byte[] checksum = createChecksum(shareB32.getVariant(), hrp, values); + byte[] combined = new byte[values.length + checksum.length]; + System.arraycopy(values, 0, combined, 0, values.length); + System.arraycopy(checksum, 0, combined, values.length, checksum.length); + StringBuilder sb = new StringBuilder(hrp.length() + 1 + combined.length); + sb.append(hrp); + sb.append('1'); + for (byte b : combined) { + sb.append(CHARSET.charAt(b)); + } + return new OctoBech32String(sb.toString()); + } + + public static OctoBech32Bucket decode(final OctoBech32String link) { + final String str = link.getValue(); + boolean lower = false; + boolean upper = false; + if (str.length() < 8) { + throw new IllegalArgumentException("Input too short: " + str.length()); + } + for (int i = 0; i < str.length(); ++i) { + char c = str.charAt(i); + if (c < 33 || c > 126) { + throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i)); + } + if (c >= 'a' && c <= 'z') { + if (upper) { + throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i)); + } + lower = true; + + } + if (c >= 'A' && c <= 'Z') { + if (lower) { + throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i)); + } + upper = true; + } + } + final int pos = str.lastIndexOf('1'); + if (pos < 1) { + throw new IllegalArgumentException("Missing human-readable part"); + } + final int dataPartLength = str.length() - 1 - pos; + if (dataPartLength < 6) { + throw new IllegalArgumentException(String.format("Data part too short: %d)", dataPartLength)); + } + byte[] values = new byte[dataPartLength]; + for (int i = 0; i < dataPartLength; ++i) { + char c = str.charAt(i + pos + 1); + if (CHARSET_REV[c] == -1) { + throw new IllegalArgumentException(String.format("Invalid Character %c, %d", c, i + pos + 1)); + } + values[i] = CHARSET_REV[c]; + } + String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT); + OctoBech32Variant encoding = verifyChecksum(hrp, values); + if (encoding == null) { + throw new IllegalArgumentException("InvalidChecksum"); + } + return new OctoBech32Bucket(encoding, hrp, Arrays.copyOfRange(values, 0, values.length - 6)); + } + + /** + * Find the polynomial with value coefficients mod the generator as 30-bit. + */ + private static int polymod(final byte[] values) { + int c = 1; + for (byte v_i : values) { + int c0 = (c >>> 25) & 0xff; + c = ((c & 0x1ffffff) << 5) ^ (v_i & 0xff); + if ((c0 & 1) != 0) { + c ^= 0x3b6a57b2; + } + if ((c0 & 2) != 0) { + c ^= 0x26508e6d; + } + if ((c0 & 4) != 0) { + c ^= 0x1ea119fa; + } + if ((c0 & 8) != 0) { + c ^= 0x3d4233dd; + } + if ((c0 & 16) != 0) { + c ^= 0x2a1462b3; + } + } + return c; + } + + /** + * Expand a HRP for use in checksum computation. + */ + private static byte[] expandHrp(final String hrp) { + int hrpLength = hrp.length(); + byte ret[] = new byte[hrpLength * 2 + 1]; + for (int i = 0; i < hrpLength; ++i) { + int c = hrp.charAt(i) & 0x7f; // Limit to standard 7-bit ASCII + ret[i] = (byte) ((c >>> 5) & 0x07); + ret[i + hrpLength + 1] = (byte) (c & 0x1f); + } + ret[hrpLength] = 0; + return ret; + } + + private static OctoBech32Variant verifyChecksum(final String hrp, final byte[] values) { + byte[] hrpExpanded = expandHrp(hrp); + byte[] combined = new byte[hrpExpanded.length + values.length]; + System.arraycopy(hrpExpanded, 0, combined, 0, hrpExpanded.length); + System.arraycopy(values, 0, combined, hrpExpanded.length, values.length); + int check = polymod(combined); + for (OctoBech32Variant type:OctoBech32Variant.values()) { + if (type.getMarker() == check) { + return type; + } + } + return null; + } + + private static byte[] createChecksum(final OctoBech32Variant encoding, final String hrp, final byte[] values) { + byte[] hrpExpanded = expandHrp(hrp); + byte[] enc = new byte[hrpExpanded.length + values.length + 6]; + System.arraycopy(hrpExpanded, 0, enc, 0, hrpExpanded.length); + System.arraycopy(values, 0, enc, hrpExpanded.length, values.length); + int mod = polymod(enc) ^ encoding.getMarker(); + byte[] ret = new byte[6]; + for (int i = 0; i < 6; ++i) { + ret[i] = (byte) ((mod >>> (5 * (5 - i))) & 31); + } + return ret; + } + + private static byte[] convertBits(byte[] data, int fromWidth, int toWidth, boolean pad) { + int acc = 0; + int bits = 0; + List result = new ArrayList<>(); + for (int i = 0; i < data.length; i++) { + int value = (data[i] & 0xff) & ((1 << fromWidth) - 1); + acc = (acc << fromWidth) | value; + bits += fromWidth; + while (bits >= toWidth) { + bits -= toWidth; + result.add((byte) ((acc >> bits) & ((1 << toWidth) - 1))); + } + } + if (pad) { + if (bits > 0) { + result.add((byte) ((acc << (toWidth - bits)) & ((1 << toWidth) - 1))); + } + } else if (bits == fromWidth || ((acc << (toWidth - bits)) & ((1 << toWidth) - 1)) != 0) { + return null; + } + byte[] output = new byte[result.size()]; + for (int i = 0; i < output.length; i++) { + output[i] = result.get(i); + } + return output; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32Bucket.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32Bucket.java new file mode 100644 index 0000000..9491fd4 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32Bucket.java @@ -0,0 +1,28 @@ +package love.distributedrebirth.no2all.octo; + +import java.util.Objects; + +public final class OctoBech32Bucket { + + private final OctoBech32Variant variant; + private final String hrp; + private final byte[] data; + + public OctoBech32Bucket(OctoBech32Variant variant, String hrp, byte[] data) { + this.variant = Objects.requireNonNull(variant); + this.hrp = Objects.requireNonNull(hrp); + this.data = Objects.requireNonNull(data); + } + + public OctoBech32Variant getVariant() { + return variant; + } + + public String getHrp() { + return hrp; + } + + public byte[] getData() { + return data; + } +} \ No newline at end of file diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32String.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32String.java new file mode 100644 index 0000000..b3d704d --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32String.java @@ -0,0 +1,20 @@ +package love.distributedrebirth.no2all.octo; + +import java.util.Objects; + +public final class OctoBech32String { + + private final String value; + + public OctoBech32String(String value) { + this.value = Objects.requireNonNull(value); + } + + public String getValue() { + return value; + } + + public static OctoBech32String of(String value) { + return new OctoBech32String(value); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32Variant.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32Variant.java new file mode 100644 index 0000000..1f91ad8 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBech32Variant.java @@ -0,0 +1,19 @@ +package love.distributedrebirth.no2all.octo; + +public enum OctoBech32Variant { + //[BIP-0173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki) + BECH32(1), + //[BIP-0350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki) + BECH32M(0x2bc830a3), + ; + + private final int marker; + + private OctoBech32Variant(int marker) { + this.marker = marker; + } + + public int getMarker() { + return marker; + } +} \ No newline at end of file diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChain.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChain.java new file mode 100644 index 0000000..6a14f37 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChain.java @@ -0,0 +1,100 @@ +package love.distributedrebirth.no2all.octo; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class OctoBitChain { + + private static final int INDEX_OF_FIRST = 0; + private final List frames = new ArrayList<>(); + + public OctoBitChain() { + } + + public OctoBitChain(byte[] data) { + this(ByteBuffer.wrap(data)); + } + + public OctoBitChain(ByteBuffer buffer) { + while (buffer.hasRemaining()) { + byte frameType = buffer.get(); + int frameLength = buffer.get(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + for (int i = 0; i < frameLength; i++) { + baos.write(buffer.get()); + } + frames.add(new OctoBitChainFrame(frameType, baos.toByteArray())); + } + } + + public void clear() { + frames.clear(); + } + + public int getFrameCount() { + return frames.size(); + } + + public void addFrame(byte type, byte[] data) { + frames.add(new OctoBitChainFrame(type, Objects.requireNonNull(data))); + } + + public void addFrame(OctoBitChainFrameType type, byte[] data) { + addFrame((byte) Objects.requireNonNull(type).getTypeByte(), Objects.requireNonNull(data)); + } + + public List getFramesByType(OctoBitChainFrameType type) { + return getFramesByType((byte)Objects.requireNonNull(type).getTypeByte()); + } + + public List getFramesByType(byte type) { + List result = new ArrayList<>(); + int framesLength = frames.size(); + for (int frameIdx = 0; frameIdx < framesLength; frameIdx++) { + OctoBitChainFrame frame = frames.get(frameIdx); + if (frame.getType() == type) { + result.add(frame); + } + } + return result; + } + + public byte[] getFirstDataOfType(OctoBitChainFrameType type) { + return getFirstDataOfType((byte)Objects.requireNonNull(type).getTypeByte()); + } + + public byte[] getFirstDataOfType(byte type) { + List frames = getFramesByType(type); + if (frames.isEmpty()) { + return null; + } + return frames.get(INDEX_OF_FIRST).getData(); + } + + public void writeOutputStream(OutputStream out) throws IOException { + int framesLength = frames.size(); + for (int frameIdx = 0; frameIdx < framesLength; frameIdx++) { + OctoBitChainFrame frame = frames.get(frameIdx); + byte[] data = frame.getData(); + out.write(frame.getType()); + out.write(data.length); + for (int dataIdx = 0; dataIdx < data.length; dataIdx++) { + out.write(data[dataIdx]); + } + } + } + + public byte[] toByteArray() { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + writeOutputStream(baos); + } catch (IOException ignoredOnBAOS) { + } + return baos.toByteArray(); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChainFrame.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChainFrame.java new file mode 100644 index 0000000..54c435e --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChainFrame.java @@ -0,0 +1,22 @@ +package love.distributedrebirth.no2all.octo; + +import java.util.Objects; + +public final class OctoBitChainFrame { + + private final byte type; + private final byte[] data; + + public OctoBitChainFrame(byte type, byte[] data) { + this.type = type; + this.data = Objects.requireNonNull(data); + } + + public byte getType() { + return type; + } + + public byte[] getData() { + return data; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChainFrameType.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChainFrameType.java new file mode 100644 index 0000000..62039fb --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitChainFrameType.java @@ -0,0 +1,6 @@ +package love.distributedrebirth.no2all.octo; + +public interface OctoBitChainFrameType { + + byte getTypeByte(); +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitConverter.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitConverter.java new file mode 100644 index 0000000..5f1e618 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitConverter.java @@ -0,0 +1,8 @@ +package love.distributedrebirth.no2all.octo; + +public interface OctoBitConverter { + + T fromBytes(byte[] data); + + byte[] toBytes(T value); +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitFormat.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitFormat.java new file mode 100644 index 0000000..c28d272 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/OctoBitFormat.java @@ -0,0 +1,39 @@ +package love.distributedrebirth.no2all.octo; + +import java.nio.charset.StandardCharsets; + +import love.distributedrebirth.no2all.octo.conv.OctoConvSIntBigIndian; +import love.distributedrebirth.no2all.octo.conv.OctoConvSLongBigIndian; +import love.distributedrebirth.no2all.octo.conv.OctoConvStringCharset; +import love.distributedrebirth.no2all.octo.conv.OctoConvStringHex; +import love.distributedrebirth.no2all.octo.conv.OctoConvStringHexUtf8; +import love.distributedrebirth.no2all.octo.conv.OctoConvStringIDN; +import love.distributedrebirth.no2all.octo.conv.OctoConvUIntBigIndian; + +public final class OctoBitFormat { + + public static final OctoBitConverter BI_UI32 = new OctoConvUIntBigIndian(); + public static final OctoBitConverter BI_SI32 = new OctoConvSIntBigIndian(); + public static final OctoBitConverter BI_SI64 = new OctoConvSLongBigIndian(); + public static final OctoBitConverter ASCII = new OctoConvStringCharset(StandardCharsets.US_ASCII); + public static final OctoBitConverter UTF8 = new OctoConvStringCharset(StandardCharsets.UTF_8); + public static final OctoBitConverter IDN = new OctoConvStringIDN(); + public static final OctoBitConverter HEX = new OctoConvStringHex(); + + // October is month 8 and only a real language has it own white space program + public static final OctoBitConverter HEX_CCP = new OctoConvStringHexUtf8(new String[] { + "\u3000","\u205F","\u202F","\u200A","\u2009","\u2008","\u2007","\u2006", + "\u2005","\u2004","\u2003","\u2002","\u2001","\u2000","\u00A0","\u0020" + }); + + public static final OctoBitConverter HEX_GREEK = new OctoConvStringHexUtf8(new String[] { + "ō","α","β","γ","δ","ε","ϝ","ζ","η","θ","ι","κ","λ","μ","ν","ξ" + }); + + public static final OctoBitConverter HEX_DIPAVALI = new OctoConvStringHexUtf8(new String[] { + "˧˥˩","˧˩˥","˧˥˦","˧˩˨","˧˦˦","˧˨˨","˧˥˥","˧˩˩","˥˩˧","˩˥˧","˥˦˧","˩˨˧","˦˦˧","˨˨˧","˥˥˧","˩˩˧" + }); // note: In real unicode renderer this (_LRPATH) renders as a single glyph per hex nibble. + + private OctoBitFormat() { + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvSIntBigIndian.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvSIntBigIndian.java new file mode 100644 index 0000000..950f90e --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvSIntBigIndian.java @@ -0,0 +1,27 @@ +package love.distributedrebirth.no2all.octo.conv; + +import love.distributedrebirth.no2all.octo.OctoBitConverter; + +public final class OctoConvSIntBigIndian implements OctoBitConverter { + + @Override + public Integer fromBytes(byte[] data) { + int result = 0; + result += (data[0] << 0) & 0xFF; + result += (data[1] << 8) & 0xFF00; + result += (data[2] << 16) & 0xFF0000; + result += (data[3] << 24) & 0xFF000000; + return result; + } + + @Override + public byte[] toBytes(Integer value) { + int data = value.intValue(); + byte[] result = new byte[4]; + result[0] = (byte)(data >> 0); + result[1] = (byte)(data >> 8); + result[2] = (byte)(data >> 16); + result[3] = (byte)(data >> 24); + return result; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvSLongBigIndian.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvSLongBigIndian.java new file mode 100644 index 0000000..3b9f329 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvSLongBigIndian.java @@ -0,0 +1,35 @@ +package love.distributedrebirth.no2all.octo.conv; + +import love.distributedrebirth.no2all.octo.OctoBitConverter; + +public final class OctoConvSLongBigIndian implements OctoBitConverter { + + @Override + public Long fromBytes(byte[] data) { + long result = 0; + result += (data[0] << 0) & 0xFFL; + result += (data[1] << 8) & 0xFF00L; + result += (data[2] << 16) & 0xFF0000L; + result += (data[3] << 24) & 0xFF000000L; + result += ((long)data[4] << 32) & 0xFF00000000L; + result += ((long)data[5] << 40) & 0xFF0000000000L; + result += ((long)data[6] << 48) & 0xFF000000000000L; + result += ((long)data[7] << 56) & 0xFF00000000000000L; + return result; + } + + @Override + public byte[] toBytes(Long value) { + long data = value.longValue(); + byte[] result = new byte[8]; + result[0] = (byte)(data >> 0); + result[1] = (byte)(data >> 8); + result[2] = (byte)(data >> 16); + result[3] = (byte)(data >> 24); + result[4] = (byte)(data >> 32); + result[5] = (byte)(data >> 40); + result[6] = (byte)(data >> 48); + result[7] = (byte)(data >> 56); + return result; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringCharset.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringCharset.java new file mode 100644 index 0000000..3c000f2 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringCharset.java @@ -0,0 +1,25 @@ +package love.distributedrebirth.no2all.octo.conv; + +import java.nio.charset.Charset; +import java.util.Objects; + +import love.distributedrebirth.no2all.octo.OctoBitConverter; + +public final class OctoConvStringCharset implements OctoBitConverter { + + private final Charset charset; + + public OctoConvStringCharset(Charset charset) { + this.charset = Objects.requireNonNull(charset); + } + + @Override + public String fromBytes(byte[] data) { + return new String(data, charset); + } + + @Override + public byte[] toBytes(String value) { + return value.getBytes(charset); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringHex.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringHex.java new file mode 100644 index 0000000..5d08bb7 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringHex.java @@ -0,0 +1,47 @@ +package love.distributedrebirth.no2all.octo.conv; + +import love.distributedrebirth.no2all.octo.OctoBitConverter; + +public final class OctoConvStringHex implements OctoBitConverter { + + private final char[] valueToHex = "0123456789abcdef".toCharArray(); + private final byte[] valueToBytes = new byte[128]; + + public OctoConvStringHex() { + for (int i = 0; i < valueToBytes.length; i++) { + valueToBytes[i] = (byte)-1; + } + for (int i = 0; i < valueToHex.length; i++) { + char c = valueToHex[i]; + valueToBytes[c] = (byte)i; + if (!Character.isDigit(c)) { + valueToBytes[Character.toUpperCase(c)] = (byte)i; + } + } + } + + @Override + public String fromBytes(byte[] data) { + int dataLength = data.length; + char[] hexChars = new char[dataLength * 2]; + for (int j = 0; j < dataLength; j++) { + int v = data[j] & 0xFF; + int idx = j * 2; + hexChars[idx] = valueToHex[v >>> 4]; + hexChars[idx + 1] = valueToHex[v & 0x0F]; + } + return new String(hexChars); + } + + @Override + public byte[] toBytes(String value) { + int valueLength = value.length(); + byte[] buf = new byte[valueLength >>> 1]; + for (int i = 0; i < valueLength; i += 2) { + byte v0 = valueToBytes[value.charAt(i)]; + byte v1 = valueToBytes[value.charAt(i + 1)]; + buf[i / 2] = (byte) ((v0 << 4) + v1); + } + return buf; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringHexUtf8.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringHexUtf8.java new file mode 100644 index 0000000..a7929b7 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringHexUtf8.java @@ -0,0 +1,61 @@ +package love.distributedrebirth.no2all.octo.conv; + +import java.io.ByteArrayOutputStream; +import java.util.HashMap; +import java.util.Map; +import java.util.PrimitiveIterator.OfInt; + +import love.distributedrebirth.no2all.octo.OctoBitConverter; + +public final class OctoConvStringHexUtf8 implements OctoBitConverter { + + private final String[] valueToHex; + private final Map valueToBytes = new HashMap<>(); + + public OctoConvStringHexUtf8(String[] valueToHex) { + if (valueToHex.length != 16) { + throw new IllegalArgumentException("Need 16 chars for hex, got: " + valueToHex.length); + } + this.valueToHex = valueToHex; + for (int i = 0; i < valueToHex.length; i++) { + valueToBytes.put(valueToHex[i], (byte)i); + } + } + + @Override + public String fromBytes(byte[] data) { + StringBuilder buf = new StringBuilder(); + int dataLength = data.length; + for (int j = 0; j < dataLength; j++) { + int v = data[j] & 0xFF; + buf.append(valueToHex[v >>> 4]); + buf.append(valueToHex[v & 0x0F]); + } + return buf.toString(); + } + + @Override + public byte[] toBytes(String value) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + StringBuilder buf = new StringBuilder(); + Byte digitPrev = null; + OfInt i = value.codePoints().iterator(); + while (i.hasNext()) { + buf.appendCodePoint(i.nextInt()); + String digitHex = buf.toString(); + if (!valueToBytes.containsKey(digitHex)) { + continue; + } + buf = new StringBuilder(); + if (digitPrev == null) { + digitPrev = valueToBytes.get(digitHex); + } else { + byte digit = valueToBytes.get(digitHex); + byte v = (byte) ((digitPrev << 4) + digit); + baos.write(v); + digitPrev = null; + } + } + return baos.toByteArray(); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringIDN.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringIDN.java new file mode 100644 index 0000000..e0776a5 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringIDN.java @@ -0,0 +1,19 @@ +package love.distributedrebirth.no2all.octo.conv; + +import java.net.IDN; +import java.nio.charset.StandardCharsets; + +import love.distributedrebirth.no2all.octo.OctoBitConverter; + +public final class OctoConvStringIDN implements OctoBitConverter { + + @Override + public String fromBytes(byte[] data) { + return IDN.toUnicode(new String(data, StandardCharsets.US_ASCII), IDN.ALLOW_UNASSIGNED); + } + + @Override + public byte[] toBytes(String value) { + return IDN.toASCII(value, IDN.ALLOW_UNASSIGNED).getBytes(StandardCharsets.US_ASCII); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvUIntBigIndian.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvUIntBigIndian.java new file mode 100644 index 0000000..e193eca --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/conv/OctoConvUIntBigIndian.java @@ -0,0 +1,27 @@ +package love.distributedrebirth.no2all.octo.conv; + +import love.distributedrebirth.no2all.octo.OctoBitConverter; + +public final class OctoConvUIntBigIndian implements OctoBitConverter { + + @Override + public Long fromBytes(byte[] data) { + long result = 0; + result += (data[0] << 0) & 0xFF; + result += (data[1] << 8) & 0xFF00; + result += (data[2] << 16) & 0xFF0000; + result += (data[3] << 24) & 0xFF000000; + return result; + } + + @Override + public byte[] toBytes(Long value) { + long data = value.longValue(); + byte[] result = new byte[4]; + result[0] = (byte)(data >> 0); + result[1] = (byte)(data >> 8); + result[2] = (byte)(data >> 16); + result[3] = (byte)(data >> 24); + return result; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrust.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrust.java new file mode 100644 index 0000000..3ca2304 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrust.java @@ -0,0 +1,39 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.math.BigInteger; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +public final class OctoTrust { + + public static final String KEY_SPEC = "secp256k1"; + public static final int KEY_LENGTH = 32; + private static final BigInteger MAXPRIVATEKEY = new BigInteger( + "00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140", 16); + + public static SecureRandom createSecureRandom() { + try { + return SecureRandom.getInstanceStrong(); + } catch (NoSuchAlgorithmException e) { + throw new OctoTrustException(e); + } + } + + public static byte[] generateSecretKey() { + SecureRandom secureRandom = createSecureRandom(); + byte[] privateKeyAttempt = new byte[KEY_LENGTH]; + secureRandom.nextBytes(privateKeyAttempt); + BigInteger privateKeyCheck = new BigInteger(1, privateKeyAttempt); + while (privateKeyCheck.compareTo(BigInteger.ZERO) == 0 || privateKeyCheck.compareTo(MAXPRIVATEKEY) == 1) { + secureRandom.nextBytes(privateKeyAttempt); + privateKeyCheck = new BigInteger(1, privateKeyAttempt); + } + return privateKeyAttempt; + } + + public static byte[] createRandomByteArray(int length) { + byte[] result = new byte[length]; + createSecureRandom().nextBytes(result); + return result; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAES.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAES.java new file mode 100644 index 0000000..2b6a2fa --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAES.java @@ -0,0 +1,72 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.SecretKeySpec; + +public final class OctoTrustAES { + + private static final String CIPHER = "AES/CBC/PKCS5Padding"; + private static final String KEY_SPEC = "AES"; + private static final int IV_LENGTH = 16; + private static final String PW_ALGORITHM = "PBKDF2WithHmacSHA256"; + private static final int PW_ITERATION_CNT = 65536; + private static final int PW_KEY_LENGTH = 256; + + public static IvParameterSpec createRandomIv() { + return new IvParameterSpec(OctoTrust.createRandomByteArray(IV_LENGTH)); + } + + public static boolean isSecretPasswordValid(char[] password, byte[] salt) { + try { + createSecretPassword(password, salt); + return true; + } catch (OctoTrustException e) { + return false; + } + } + + public static SecretKey createSecretPassword(char[] password, byte[] salt) { + try { + SecretKeyFactory factory = SecretKeyFactory.getInstance(PW_ALGORITHM); + KeySpec spec = new PBEKeySpec(password, salt, PW_ITERATION_CNT, PW_KEY_LENGTH); + return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), KEY_SPEC); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new OctoTrustException(e); + } + } + + public static byte[] encrypt(byte[] data, SecretKey key, IvParameterSpec iv) { + try { + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + return cipher.doFinal(data); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new OctoTrustException(e); + } + } + + public static byte[] decrypt(byte[] data, SecretKey key, IvParameterSpec iv) { + try { + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.DECRYPT_MODE, key, iv); + return cipher.doFinal(data); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new OctoTrustException(e); + } + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESCurve.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESCurve.java new file mode 100644 index 0000000..a8668ac --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESCurve.java @@ -0,0 +1,88 @@ +package love.distributedrebirth.no2all.octo.trust; + +import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Base64; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.util.Random; + +public final class OctoTrustAESCurve { + + private static final String CIPHER = "AES/CBC/PKCS5Padding"; + private static final String KEY_SPEC = "AES"; + private static final int KEY_LENGTH = 32; + private static final int IV_LENGTH = 16; + + private static byte[] bytesFromBigInteger(BigInteger n) { + byte[] b = n.toByteArray(); + if (b.length == KEY_LENGTH) { + return b; + } else if (b.length > KEY_LENGTH) { + return java.util.Arrays.copyOfRange(b, b.length - KEY_LENGTH, b.length); + } else { + byte[] buf = new byte[KEY_LENGTH]; + System.arraycopy(b, 0, buf, buf.length - b.length, b.length); + return buf; + } + } + + public static BigInteger calculateKeyAgreement(BigInteger privKey, BigInteger theirPubKey) { + ECDomainParameters param = OctoTrustAESCurveX9EC.FACTORY.getCurveEC(); + ECPrivateKeyParameters privKeyP = new ECPrivateKeyParameters(privKey, param); + byte[] compressed = new byte[] { 2 }; + byte[] val = Arrays.concatenate(compressed, bytesFromBigInteger(theirPubKey)); + ECPoint ecPoint = param.getCurve().decodePoint(val); + ECPublicKeyParameters pubKeyP = new ECPublicKeyParameters(ecPoint, param); + ECDHBasicAgreement agreement = new ECDHBasicAgreement(); + agreement.init(privKeyP); + return agreement.calculateAgreement(pubKeyP); + } + + public static String encrypt(BigInteger key, String msg) { + try { + Random r = SecureRandom.getInstanceStrong(); + byte[] iv = new byte[IV_LENGTH]; + r.nextBytes(iv); + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(bytesFromBigInteger(key), KEY_SPEC), new IvParameterSpec(iv)); + String ivBase64 = Base64.toBase64String(iv); + byte[] encryptedMsg = cipher.doFinal(msg.getBytes()); + String encryptedMsgBase64 = Base64.toBase64String(encryptedMsg); + return String.format("%s?iv=%s", encryptedMsgBase64, ivBase64); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new OctoTrustException(e); + } + } + + public static String decrypt(BigInteger key, String encryptedMsg) { + try { + int pos = encryptedMsg.indexOf("?iv="); + String msgPart = encryptedMsg.substring(0, pos); + String ivPart = encryptedMsg.substring(pos + 4); + byte[] decodedMsg = Base64.decode(msgPart); + byte[] iv = Base64.decode(ivPart); + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(bytesFromBigInteger(key), KEY_SPEC), new IvParameterSpec(iv)); + return new String(cipher.doFinal(decodedMsg)); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new OctoTrustException(e); + } + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESCurveX9EC.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESCurveX9EC.java new file mode 100644 index 0000000..2549c22 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESCurveX9EC.java @@ -0,0 +1,26 @@ +package love.distributedrebirth.no2all.octo.trust; + +import org.bouncycastle.asn1.sec.SECNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; + +public enum OctoTrustAESCurveX9EC { + FACTORY; + + private static final String CURVE_NAME = "secp256k1"; + private final X9ECParameters curveX9; + private final ECDomainParameters curveEC; + + private OctoTrustAESCurveX9EC() { + curveX9 = SECNamedCurves.getByName(CURVE_NAME); + curveEC = new ECDomainParameters(curveX9.getCurve(), curveX9.getG(), curveX9.getN(), curveX9.getH()); + } + + public X9ECParameters getCurveX9() { + return curveX9; + } + + public ECDomainParameters getCurveEC() { + return curveEC; + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustChaChaPx.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustChaChaPx.java new file mode 100644 index 0000000..fb39f88 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustChaChaPx.java @@ -0,0 +1,51 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.nio.ByteBuffer; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +public class OctoTrustChaChaPx { + + private static final String CIPHER = "ChaCha20-Poly1305"; + private static final int NONCE_LENGTH = 12; + + public static byte[] createRandomNonce() { + return OctoTrust.createRandomByteArray(NONCE_LENGTH); + } + + public byte[] encrypt(byte[] data, SecretKey key, byte[] nonce) { + try { + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(nonce)); + byte[] encrypted = cipher.doFinal(data); + return ByteBuffer.allocate(encrypted.length + NONCE_LENGTH).put(encrypted).put(nonce).array(); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new OctoTrustException(e); + } + } + + public byte[] decrypt(byte[] data, SecretKey key) { + byte[] encrypted = new byte[data.length - NONCE_LENGTH]; + byte[] nonce = new byte[NONCE_LENGTH]; + ByteBuffer bb = ByteBuffer.wrap(data); + bb.get(encrypted); + bb.get(nonce); + try { + Cipher cipher = Cipher.getInstance(CIPHER); + cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(nonce)); + return cipher.doFinal(encrypted); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new OctoTrustException(e); + } + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustException.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustException.java new file mode 100644 index 0000000..5981693 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustException.java @@ -0,0 +1,13 @@ +package love.distributedrebirth.no2all.octo.trust; + +@SuppressWarnings("serial") +public class OctoTrustException extends RuntimeException { + + public OctoTrustException(String msg) { + super(msg); + } + + public OctoTrustException(Exception e) { + super(e); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustHash.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustHash.java new file mode 100644 index 0000000..6c1415e --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustHash.java @@ -0,0 +1,21 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public final class OctoTrustHash { + + private static final String SHA_256 = "SHA-256"; + + public static MessageDigest sha256Algorithm() { + try { + return MessageDigest.getInstance(SHA_256); + } catch (NoSuchAlgorithmException e) { + throw new OctoTrustException(e); + } + } + + public static byte[] sha256(byte[] data) { + return sha256Algorithm().digest(data); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorr.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorr.java new file mode 100644 index 0000000..7322914 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorr.java @@ -0,0 +1,127 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.math.BigInteger; +import java.util.Arrays; + +public final class OctoTrustSchnorr { + + public static byte[] sign(byte[] msg, byte[] secKey, byte[] auxRand) { + if (msg.length != 32) { + throw new IllegalArgumentException("The message must be a 32-byte array."); + } + BigInteger secKey0 = bytesToBigInt(secKey); + if (!(BigInteger.ONE.compareTo(secKey0) <= 0 + && secKey0.compareTo(OctoTrustSchnorrPoint.getn().subtract(BigInteger.ONE)) <= 0)) { + throw new IllegalArgumentException("The secret key must be an integer in the range 1..n-1."); + } + OctoTrustSchnorrPoint P = OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.getG(), secKey0); + if (!P.hasEvenY()) { + secKey0 = OctoTrustSchnorrPoint.getn().subtract(secKey0); + } + int len = bigIntToBytes(secKey0).length + P.toBytes().length + msg.length; + byte[] buf = new byte[len]; + byte[] t = xor(bigIntToBytes(secKey0), OctoTrustSchnorrPoint.taggedHash("BIP0340/aux", auxRand)); + System.arraycopy(t, 0, buf, 0, t.length); + System.arraycopy(P.toBytes(), 0, buf, t.length, P.toBytes().length); + System.arraycopy(msg, 0, buf, t.length + P.toBytes().length, msg.length); + BigInteger k0 = bytesToBigInt(OctoTrustSchnorrPoint.taggedHash("BIP0340/nonce", buf)) + .mod(OctoTrustSchnorrPoint.getn()); + if (k0.compareTo(BigInteger.ZERO) == 0) { + throw new IllegalArgumentException("Failure. This happens only with negligible probability."); + } + OctoTrustSchnorrPoint R = OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.getG(), k0); + BigInteger k; + if (!R.hasEvenY()) { + k = OctoTrustSchnorrPoint.getn().subtract(k0); + } else { + k = k0; + } + len = R.toBytes().length + P.toBytes().length + msg.length; + buf = new byte[len]; + System.arraycopy(R.toBytes(), 0, buf, 0, R.toBytes().length); + System.arraycopy(P.toBytes(), 0, buf, R.toBytes().length, P.toBytes().length); + System.arraycopy(msg, 0, buf, R.toBytes().length + P.toBytes().length, msg.length); + BigInteger e = bytesToBigInt(OctoTrustSchnorrPoint.taggedHash("BIP0340/challenge", buf)) + .mod(OctoTrustSchnorrPoint.getn()); + BigInteger kes = k.add(e.multiply(secKey0)).mod(OctoTrustSchnorrPoint.getn()); + len = R.toBytes().length + bigIntToBytes(kes).length; + byte[] sig = new byte[len]; + System.arraycopy(R.toBytes(), 0, sig, 0, R.toBytes().length); + System.arraycopy(bigIntToBytes(kes), 0, sig, R.toBytes().length, bigIntToBytes(kes).length); + if (!verify(msg, P.toBytes(), sig)) { + throw new IllegalStateException("The signature does not pass verification."); + } + return sig; + } + + public static boolean verify(byte[] msg, byte[] pubkey, byte[] sig) { + if (msg.length != 32) { + throw new IllegalArgumentException("The message must be a 32-byte array."); + } + if (pubkey.length != 32) { + throw new IllegalArgumentException("The public key must be a 32-byte array."); + } + if (sig.length != 64) { + throw new IllegalArgumentException("The signature must be a 64-byte array."); + } + OctoTrustSchnorrPoint P = OctoTrustSchnorrPoint.liftX(pubkey); + if (P == null) { + return false; + } + BigInteger r = bytesToBigInt(Arrays.copyOfRange(sig, 0, 32)); + BigInteger s = bytesToBigInt(Arrays.copyOfRange(sig, 32, 64)); + if (r.compareTo(OctoTrustSchnorrPoint.getp()) >= 0 || s.compareTo(OctoTrustSchnorrPoint.getn()) >= 0) { + return false; + } + int len = 32 + pubkey.length + msg.length; + byte[] buf = new byte[len]; + System.arraycopy(sig, 0, buf, 0, 32); + System.arraycopy(pubkey, 0, buf, 32, pubkey.length); + System.arraycopy(msg, 0, buf, 32 + pubkey.length, msg.length); + BigInteger e = bytesToBigInt(OctoTrustSchnorrPoint.taggedHash("BIP0340/challenge", buf)) + .mod(OctoTrustSchnorrPoint.getn()); + OctoTrustSchnorrPoint R = OctoTrustSchnorrPoint.add(OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.getG(), s), + OctoTrustSchnorrPoint.mul(P, OctoTrustSchnorrPoint.getn().subtract(e))); + return R != null && R.hasEvenY() && R.getX().compareTo(r) == 0; + } + + public static byte[] extractPublicKey(byte[] secKey) { + BigInteger x = bytesToBigInt(secKey); + if (!(BigInteger.ONE.compareTo(x) <= 0 + && x.compareTo(OctoTrustSchnorrPoint.getn().subtract(BigInteger.ONE)) <= 0)) { + throw new IllegalArgumentException("The secret key must be an integer in the range 1..n-1."); + } + OctoTrustSchnorrPoint result = OctoTrustSchnorrPoint.mul(OctoTrustSchnorrPoint.G, x); + return OctoTrustSchnorrPoint.bytesFromPoint(result); + } + + protected static byte[] xor(byte[] b0, byte[] b1) { + if (b0.length != b1.length) { + return null; + } + byte[] result = new byte[b0.length]; + int i = 0; + for (byte b : b0) { + result[i] = (byte) (b ^ b1[i]); + i++; + } + return result; + } + + protected static byte[] bigIntToBytes(BigInteger n) { + byte[] b = n.toByteArray(); + if (b.length == 32) { + return b; + } else if (b.length > 32) { + return Arrays.copyOfRange(b, b.length - 32, b.length); + } else { + byte[] buf = new byte[32]; + System.arraycopy(b, 0, buf, buf.length - b.length, b.length); + return buf; + } + } + + protected static BigInteger bytesToBigInt(byte[] b) { + return new BigInteger(1, b); + } +} diff --git a/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorrPoint.java b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorrPoint.java new file mode 100644 index 0000000..5dc5e74 --- /dev/null +++ b/no2all-octo/src/main/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorrPoint.java @@ -0,0 +1,181 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.math.BigInteger; + +public class OctoTrustSchnorrPoint { + + final static private BigInteger p = new BigInteger( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16); + final static private BigInteger n = new BigInteger( + "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16); + + final static public OctoTrustSchnorrPoint G = new OctoTrustSchnorrPoint( + new BigInteger("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 16), + new BigInteger("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 16)); + + private static final BigInteger BI_TWO = BigInteger.valueOf(2); + private final BigInteger x; + private final BigInteger y; + + public OctoTrustSchnorrPoint(BigInteger x, BigInteger y) { + this.x = x; + this.y = y; + } + + public static BigInteger getp() { + return p; + } + + public static BigInteger getn() { + return n; + } + + public static OctoTrustSchnorrPoint getG() { + return G; + } + + public BigInteger getX() { + return x; + } + + public BigInteger getY() { + return y; + } + + public static BigInteger getX(OctoTrustSchnorrPoint point) { + return point.getX(); + } + + public static BigInteger getY(OctoTrustSchnorrPoint point) { + return point.getY(); + } + + public boolean isInfinite() { + return x == null || y == null; + } + + public static boolean isInfinite(OctoTrustSchnorrPoint point) { + return point.isInfinite(); + } + + public OctoTrustSchnorrPoint add(OctoTrustSchnorrPoint point) { + return add(this, point); + } + + public static OctoTrustSchnorrPoint add(OctoTrustSchnorrPoint point1, OctoTrustSchnorrPoint point2) { + if ((point1 != null && point2 != null && point1.isInfinite() && point2.isInfinite())) { + return infinityPoint(); + } + if (point1 == null || point1.isInfinite()) { + return point2; + } + if (point2 == null || point2.isInfinite()) { + return point1; + } + if (point1.getX().equals(point2.getX()) && !point1.getY().equals(point2.getY())) { + return infinityPoint(); + } + BigInteger lam; + if (point1.equals(point2)) { + BigInteger base = point2.getY().multiply(BI_TWO); + lam = (BigInteger.valueOf(3L).multiply(point1.getX()).multiply(point1.getX()) + .multiply(base.modPow(p.subtract(BI_TWO), p))).mod(p); + } else { + BigInteger base = point2.getX().subtract(point1.getX()); + lam = ((point2.getY().subtract(point1.getY())).multiply(base.modPow(p.subtract(BI_TWO), p))).mod(p); + } + BigInteger x3 = (lam.multiply(lam).subtract(point1.getX()).subtract(point2.getX())).mod(p); + return new OctoTrustSchnorrPoint(x3, lam.multiply(point1.getX().subtract(x3)).subtract(point1.getY()).mod(p)); + } + + public OctoTrustSchnorrPoint mul(BigInteger n) { + return mul(this, n); + } + + public static OctoTrustSchnorrPoint mul(OctoTrustSchnorrPoint point, BigInteger n) { + OctoTrustSchnorrPoint R = null; + for (int i = 0; i < 256; i++) { + if (n.shiftRight(i).and(BigInteger.ONE).compareTo(BigInteger.ZERO) > 0) { + R = add(R, point); + } + point = add(point, point); + } + return R; + } + + public boolean hasEvenY() { + return hasEvenY(this); + } + + public static boolean hasEvenY(OctoTrustSchnorrPoint point) { + if (point == null) { + return false; + } + if (point.getY() == null) { // added for junit + return false; + } + return point.getY().mod(BI_TWO).compareTo(BigInteger.ZERO) == 0; + } + + public static boolean isSquare(BigInteger x) { + return x.modPow(p.subtract(BigInteger.ONE).mod(BI_TWO), p).longValue() == 1L; + } + + public boolean hasSquareY() { + return hasSquareY(this); + } + + public static boolean hasSquareY(OctoTrustSchnorrPoint point) { + return isSquare(point.getY()); + } + + public static byte[] taggedHash(String tag, byte[] msg) { + byte[] tagHash = OctoTrustHash.sha256(tag.getBytes()); + int len = (tagHash.length * 2) + msg.length; + byte[] buf = new byte[len]; + System.arraycopy(tagHash, 0, buf, 0, tagHash.length); + System.arraycopy(tagHash, 0, buf, tagHash.length, tagHash.length); + System.arraycopy(msg, 0, buf, tagHash.length * 2, msg.length); + return OctoTrustHash.sha256(buf); + } + + public static byte[] genPubKey(byte[] secKey) { + BigInteger x = OctoTrustSchnorr.bytesToBigInt(secKey); + if (!(BigInteger.ONE.compareTo(x) <= 0 && x.compareTo(getn().subtract(BigInteger.ONE)) <= 0)) { + throw new IllegalArgumentException("The secret key must be an integer in the range 1..n-1."); + } + OctoTrustSchnorrPoint ret = OctoTrustSchnorrPoint.mul(G, x); + return bytesFromPoint(ret); + } + + public byte[] toBytes() { + return bytesFromPoint(this); + } + + public static byte[] bytesFromPoint(OctoTrustSchnorrPoint point) { + return OctoTrustSchnorr.bigIntToBytes(point.getX()); + } + + public static OctoTrustSchnorrPoint liftX(byte[] b) { + BigInteger x = OctoTrustSchnorr.bytesToBigInt(b); + if (x.compareTo(p) >= 0) { + return null; + } + BigInteger y_sq = x.modPow(BigInteger.valueOf(3L), p).add(BigInteger.valueOf(7L)).mod(p); + BigInteger y = y_sq.modPow(p.add(BigInteger.ONE).divide(BigInteger.valueOf(4L)), p); + if (y.modPow(BI_TWO, p).compareTo(y_sq) != 0) { + return null; + } else { + return new OctoTrustSchnorrPoint(x, y.and(BigInteger.ONE).compareTo(BigInteger.ZERO) == 0 ? y : p.subtract(y)); + } + } + + public static OctoTrustSchnorrPoint infinityPoint() { + return new OctoTrustSchnorrPoint(null, null); + } + + public boolean equals(OctoTrustSchnorrPoint point) { + return getX().equals(point.getX()) && getY().equals(point.getY()); + } + +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBech32Test.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBech32Test.java new file mode 100644 index 0000000..385514e --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBech32Test.java @@ -0,0 +1,24 @@ +package love.distributedrebirth.no2all.octo; + +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class OctoBech32Test { + + @Test + public void testToBech32() { + byte[] data = "abcd".getBytes(StandardCharsets.UTF_8); + String b32 = OctoBech32.toBech32("junit", data).getValue(); + Assertions.assertEquals("junit1v93xxeqlhr26v", b32); + } + + @Test + public void testFromBech32() { + String b32 = "junit1v93xxeqlhr26v"; + byte[] data = OctoBech32.fromBech32(OctoBech32String.of(b32)).getData(); + String result = new String(data, StandardCharsets.UTF_8); + Assertions.assertEquals("abcd", result); + } +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBitChainTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBitChainTest.java new file mode 100644 index 0000000..f35606d --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBitChainTest.java @@ -0,0 +1,41 @@ +package love.distributedrebirth.no2all.octo; + +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class OctoBitChainTest { + + private enum XHtml6 implements OctoBitChainFrameType { + HEAD, BODY, SOUL; + + static String PREFIX = XHtml6.class.getSimpleName().toLowerCase(); + + @Override + public byte getTypeByte() { + return (byte)ordinal(); + } + } + + @Test + public void testCustomTLV() { + // Setup test + OctoBitChain chain = new OctoBitChain(); + chain.addFrame(XHtml6.HEAD, "".getBytes(StandardCharsets.UTF_8)); + chain.addFrame(XHtml6.HEAD, "".getBytes(StandardCharsets.UTF_8)); + chain.addFrame(XHtml6.HEAD, "".getBytes(StandardCharsets.UTF_8)); + chain.addFrame(XHtml6.BODY, "".getBytes(StandardCharsets.UTF_8)); + chain.addFrame(XHtml6.SOUL, "𑀳𑁂𑀮𑀺𑀉𑁄𑀤𑁄𑀭𑁂𑀡 𑀪𑀸𑀕".getBytes(StandardCharsets.UTF_8)); + // Convert over wire + String linkB32 = OctoBech32.toBech32(XHtml6.PREFIX, chain.toByteArray()).getValue(); + byte[] linkB32Data = OctoBech32.fromBech32(new OctoBech32String(linkB32)).getData(); + // Test byte parser + OctoBitChain test = new OctoBitChain(linkB32Data); + Assertions.assertEquals(5, test.getFrameCount()); + Assertions.assertEquals(3, test.getFramesByType(XHtml6.HEAD).size()); + Assertions.assertEquals("𑀳𑁂𑀮𑀺𑀉𑁄𑀤𑁄𑀭𑁂𑀡 𑀪𑀸𑀕", new String(test.getFirstDataOfType(XHtml6.SOUL), StandardCharsets.UTF_8)); + // Test full binary + Assertions.assertArrayEquals(chain.toByteArray(), test.toByteArray()); + } +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBitFormatTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBitFormatTest.java new file mode 100644 index 0000000..0858c9e --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/OctoBitFormatTest.java @@ -0,0 +1,27 @@ +package love.distributedrebirth.no2all.octo; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.octo.trust.OctoTrust; + +public class OctoBitFormatTest { + + @Test + public void testFormatCCP() { + byte[] data = OctoTrust.createRandomByteArray(32); + String hex = OctoBitFormat.HEX_CCP.fromBytes(data); + byte[] result = OctoBitFormat.HEX_CCP.toBytes(hex); + Assertions.assertArrayEquals(data, result); + System.out.println("HEX_CCP: \"" + hex + "\" <== 32 bytes"); + } + + @Test + public void testFormatDipavali() { + byte[] data = OctoTrust.createRandomByteArray(11); + String hex = OctoBitFormat.HEX_DIPAVALI.fromBytes(data); + byte[] result = OctoBitFormat.HEX_DIPAVALI.toBytes(hex); + Assertions.assertArrayEquals(data, result); + System.out.println("HEX_DIPAVALI: \"" + hex + "\" <== 11 bytes, copy to text editor"); + } +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvSIntBigIndianTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvSIntBigIndianTest.java new file mode 100644 index 0000000..3278eb3 --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvSIntBigIndianTest.java @@ -0,0 +1,21 @@ +package love.distributedrebirth.no2all.octo.conv; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class OctoConvSIntBigIndianTest { + + @Test + public void testI32() { + Assertions.assertEquals(0, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(0))); + Assertions.assertEquals(1, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(1))); + Assertions.assertEquals(13, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(13))); + Assertions.assertEquals(4096, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(4096))); + Assertions.assertEquals(782356423, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(782356423))); + Assertions.assertEquals(1992356423, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(1992356423))); + Assertions.assertEquals(-88378353, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(-88378353))); + Assertions.assertEquals(-1, OctoBitFormat.BI_SI32.fromBytes(OctoBitFormat.BI_SI32.toBytes(-1))); + } +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvSLongBigIndianTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvSLongBigIndianTest.java new file mode 100644 index 0000000..33f93e7 --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvSLongBigIndianTest.java @@ -0,0 +1,22 @@ +package love.distributedrebirth.no2all.octo.conv; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class OctoConvSLongBigIndianTest { + + @Test + public void testI64() { + Assertions.assertEquals(0L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(0L))); + Assertions.assertEquals(1L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(1L))); + Assertions.assertEquals(13L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(13L))); + Assertions.assertEquals(4096L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(4096L))); + Assertions.assertEquals(1992356423L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(1992356423L))); + Assertions.assertEquals(81992356423L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(81992356423L))); + Assertions.assertEquals(7825780943356423L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(7825780943356423L))); + Assertions.assertEquals(-89548957835783434L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(-89548957835783434L))); + Assertions.assertEquals(-1L, OctoBitFormat.BI_SI64.fromBytes(OctoBitFormat.BI_SI64.toBytes(-1L))); + } +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringIDNTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringIDNTest.java new file mode 100644 index 0000000..8116888 --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/conv/OctoConvStringIDNTest.java @@ -0,0 +1,35 @@ +package love.distributedrebirth.no2all.octo.conv; + +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class OctoConvStringIDNTest { + + String validUrlFB = "facebook.com"; + String validUrlAbc = "www.google.com"; + String[][] testIDN = { + {"www.fаcebook.com", "www.xn--fcebook-2fg.com"}, + {"www.faϲebook.com", "www.xn--faebook-6pf.com"}, + {"www.facеbook.com", "www.xn--facbook-9gg.com"}, + {"www.facebооk.com", "www.xn--facebk-0qfa.com"}, + {"ԝԝԝ.facebook.com", "xn--07aaa.facebook.com"}, + {"ԝԝԝ.fаϲеbооk.com", "xn--07aaa.xn--fbk-qzc85c5a5da.com"}, + {"www.googlе.com", "www.xn--googl-3we.com"}, + {"www.gооgle.com", "www.xn--ggle-55da.com"}, + {"ԝԝԝ.google.com", "xn--07aaa.google.com"}, + {"ԝԝԝ.gооglе.com", "xn--07aaa.xn--ggl-tdd6ba.com"}, + }; + + @Test + public void testFormatIDN() { + OctoConvStringIDN IDN = new OctoConvStringIDN(); + for (String[] test: testIDN) { + String ascii = new String(IDN.toBytes(test[0]), StandardCharsets.US_ASCII); + Assertions.assertEquals(test[1], ascii); + Assertions.assertFalse(validUrlFB.equals(ascii)); + Assertions.assertFalse(validUrlAbc.equals(ascii)); + } + } +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctaTrustAESCurveTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctaTrustAESCurveTest.java new file mode 100644 index 0000000..c2ca565 --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctaTrustAESCurveTest.java @@ -0,0 +1,39 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.math.BigInteger; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class OctaTrustAESCurveTest { + + private BigInteger createSharedKey() { + byte[] secKey = OctoBitFormat.HEX.toBytes("0000000000000000000000000000000000000000000000000000000000002222"); + byte[] pubKey = OctoBitFormat.HEX.toBytes("2ef93f01cd2493e04235a6b87b10d3c4a74e2a7eb7c3caf168268f6af73314b5"); + return OctoTrustAESCurve.calculateKeyAgreement(new BigInteger(secKey), new BigInteger(pubKey)); + } + + @Test + public void testCypherSimple() throws Exception { + BigInteger sharedKey = createSharedKey(); + String wireMessage = OctoTrustAESCurve.encrypt(sharedKey, "hi"); + String decodedMessage = OctoTrustAESCurve.decrypt(sharedKey, wireMessage); + Assertions.assertEquals("733642b9f465207b29ca9070b17104db5e18f6fb6de1a88ee494f00f01eaca7", sharedKey.toString(16)); + Assertions.assertEquals("hi", decodedMessage); + } + + @Test + public void testCypherRepeat() throws Exception { + BigInteger sharedKey = createSharedKey(); + Set results = new HashSet<>(); + for (int i = 0; i < 33; i++) { + String wireMessage = OctoTrustAESCurve.encrypt(sharedKey, "foo"); + Assertions.assertFalse(results.contains(wireMessage)); + results.add(wireMessage); + } + } +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESTest.java new file mode 100644 index 0000000..e09c1a5 --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctoTrustAESTest.java @@ -0,0 +1,67 @@ +package love.distributedrebirth.no2all.octo.trust; + +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class OctoTrustAESTest { + + @Test + public void testPrivateKeyPassword() throws Exception { + byte[] salt = "1234567890".getBytes(); + SecretKey k1 = OctoTrustAES.createSecretPassword("foobar".toCharArray(), salt); + SecretKey k2 = OctoTrustAES.createSecretPassword("foobar".toCharArray(), salt); + IvParameterSpec iv1 = OctoTrustAES.createRandomIv(); + IvParameterSpec iv2 = iv1; + + byte[] nsec = OctoTrust.generateSecretKey(); + System.out.println("nsec: " + OctoBitFormat.HEX.fromBytes(nsec)); + byte[] nsecpw = OctoTrustAES.encrypt(nsec, k1, iv1); + System.out.println("nsecpw: " + OctoBitFormat.HEX.fromBytes(nsecpw)); + byte[] nsec2 = OctoTrustAES.decrypt(nsecpw, k2, iv2); + System.out.println("nsec2: " + OctoBitFormat.HEX.fromBytes(nsec2)); + Assertions.assertArrayEquals(nsec, nsec2); +// +// // TEST END HERE +// // wip +// +// //String algo = "PBKDF2WithHmacSHA256"; +// String algo = "PBEWithSHA1AndDESede"; +// AlgorithmParameters algparms = AlgorithmParameters.getInstance(algo); +// PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 33); +// algparms.init(pbeParamSpec); +// //algparms.init(new IvParameterSpec(iv)); // for "AES" params +// EncryptedPrivateKeyInfo encinfo = new EncryptedPrivateKeyInfo(algparms, nsecpw); +// +// byte[] encryptedPkcs8 = encinfo.getEncoded(); +// String b32Pkcs8 = OctoBech32.toBech32("ntest", encryptedPkcs8).getValue(); +// System.out.println("pkcs8-hex: " + OctoBitFormat.HEX.fromBytes(encryptedPkcs8)); +// System.out.println("pkcs8-b32: " + b32Pkcs8); +// byte[] linkData = OctoBech32.fromBech32(OctoBech32String.of(b32Pkcs8)).getData(); +// +// EncryptedPrivateKeyInfo info = new EncryptedPrivateKeyInfo(linkData); +// byte[] nsec3 = info.getEncryptedData(); +// System.out.println("asn.name: " + info.getAlgName()); +// System.out.println("asn.param: " + info.getAlgParameters()); +// System.out.println("nsec3: " + OctoBitFormat.HEX.fromBytes(nsec3)); +// Assertions.assertArrayEquals(nsecpw, nsec3); +// +// Cipher cipher = Cipher.getInstance(info.getAlgName()); +// PBEKeySpec pbeKeySpec = new PBEKeySpec("foobar".toCharArray()); +// SecretKeyFactory secFac = SecretKeyFactory.getInstance(info.getAlgName()); +// Key pbeKey = secFac.generateSecret(pbeKeySpec); +// AlgorithmParameters algParams = info.getAlgParameters(); +// cipher.init(Cipher.DECRYPT_MODE, pbeKey, algParams); + + // err + //KeySpec pkcs8KeySpec = info.getKeySpec(cipher); + //KeyFactory kf = KeyFactory.getInstance("PBEWithSHA1AndDESede"); + //byte[] nsec4 = kf.generatePrivate(pkcs8KeySpec).getEncoded(); + //System.out.println("nsec4: " + NoStrOctoBitFormat.HEX.fromBytes(nsec4)); + } + +} diff --git a/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorrTest.java b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorrTest.java new file mode 100644 index 0000000..3cf1e3d --- /dev/null +++ b/no2all-octo/src/test/java/love/distributedrebirth/no2all/octo/trust/OctoTrustSchnorrTest.java @@ -0,0 +1,82 @@ +package love.distributedrebirth.no2all.octo.trust; + +import java.util.Arrays; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import love.distributedrebirth.no2all.octo.OctoBitFormat; + +public class OctoTrustSchnorrTest { + + String[] data = { + // index,secret key,public key, aux rand, message,signature,verification + // result,comment + "0,0000000000000000000000000000000000000000000000000000000000000003,F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9,0000000000000000000000000000000000000000000000000000000000000000,0000000000000000000000000000000000000000000000000000000000000000,E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0,TRUE", + "1,B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,0000000000000000000000000000000000000000000000000000000000000001,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A,TRUE", + "2,C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9,DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8,C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906,7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C,5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7,TRUE", + "3,0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710,25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3,TRUE,test fails if msg is reduced modulo p or n", + "4,,D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9,,4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703,00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4,TRUE", + "5,,EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key not on the curve", + "6,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2,FALSE,has_even_y(R) is false", + "7,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD,FALSE,negated message", + "8,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6,FALSE,negated s value", + "9,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0", + "10,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197,FALSE,sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1", + "11,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is not an X coordinate on the curve", + "12,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,sig[0:32] is equal to field size", + "13,,DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,FALSE,sig[32:64] is equal to curve order", + "14,,FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30,,243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89,6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B,FALSE,public key is not a valid X coordinate because it exceeds the field size", }; + + + @Test + public void testDataSet() throws Exception { + boolean allSigsPassed = true; + boolean allVerifsPassed = true; + for (int i = 0; i < data.length; i++) { + String[] s = data[i].split(","); + String index = s[0]; + String seckey = s[1]; + String pubkey = s[2]; + String auxrand = s[3]; + String msg = s[4]; + String sig = s[5]; + String result = s[6]; + String comment = (s.length == 8) ? s[7] : null; + byte[] _pubkey = OctoBitFormat.HEX.toBytes(pubkey); + byte[] _auxrand = OctoBitFormat.HEX.toBytes(auxrand); + byte[] _msg = OctoBitFormat.HEX.toBytes(msg); + byte[] _sig = OctoBitFormat.HEX.toBytes(sig); + boolean _result = result.equals("TRUE"); + + if (seckey != null && seckey.length() > 0) { + byte[] _seckey = OctoBitFormat.HEX.toBytes(seckey); + byte[] _pubkeyActual = OctoTrustSchnorrPoint.genPubKey(_seckey); + if (!Arrays.equals(_pubkey, _pubkeyActual)) { + System.out.println(" * Failed key generation: " + index); + System.out.println("\tExpected key:" + OctoBitFormat.HEX.fromBytes(_pubkey)); + System.out.println("\tActual key:" + OctoBitFormat.HEX.fromBytes(_pubkeyActual)); + allSigsPassed = false; + } + byte[] _sigActual = OctoTrustSchnorr.sign(_msg, _seckey, _auxrand); + if (!Arrays.equals(_sig, _sigActual)) { + System.out.println(" * Failed signing test: " + index); + System.out.println("\tExpected signature:" + OctoBitFormat.HEX.fromBytes(_sig)); + System.out.println("\tActual signature:" + OctoBitFormat.HEX.fromBytes(_sigActual)); + allSigsPassed = false; + } + } + boolean resultActual = OctoTrustSchnorr.verify(_msg, _pubkey, _sig); + if (_result != resultActual) { + System.out.println(" * Failed verification test."); + System.out.println("\tExpected verification result:" + result); + System.out.println("\tActual verification result:" + resultActual); + if (comment != null) { + System.out.println("\tComment:" + comment); + } + allVerifsPassed = false; + } + } + Assertions.assertTrue(allSigsPassed && allVerifsPassed); + } +} diff --git a/no2all-react-nostr/pom.xml b/no2all-react-nostr/pom.xml new file mode 100644 index 0000000..ff6665a --- /dev/null +++ b/no2all-react-nostr/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-react-nostr + No2All-React-NoStr + + + ${project.groupId} + no2all-nostr + + + ${project.groupId} + no2all-react-wire + + + ${project.groupId} + no2all-wire-ojw + test + + + \ No newline at end of file diff --git a/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRelayFetchInfo.java b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRelayFetchInfo.java new file mode 100644 index 0000000..d40fc8f --- /dev/null +++ b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRelayFetchInfo.java @@ -0,0 +1,32 @@ +package love.distributedrebirth.no2all.react.nostr.client; + +import java.io.IOException; +import java.io.StringReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; + + +import love.distributedrebirth.no2all.nostr.model.NoStrRelayInfo; + +import java.net.http.HttpResponse.BodyHandlers; + +import jakarta.json.Json; +import jakarta.json.JsonReader; + +public class NoStrRelayFetchInfo { + + public NoStrRelayInfo fetchRelayInfo(URI uri) throws IOException, InterruptedException { + HttpRequest request = HttpRequest.newBuilder(uri).header("Accept", "application/nostr+json").build(); + HttpResponse response = HttpClient.newHttpClient().send(request, BodyHandlers.ofString()); + if (response.statusCode() != 200) { + return null; + } + String contentJson = response.body(); + try (JsonReader read = Json.createReader(new StringReader(contentJson))) { + NoStrRelayInfo result = new NoStrRelayInfo(read.readObject()); + return result; + } + } +} diff --git a/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRelayReadType.java b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRelayReadType.java new file mode 100644 index 0000000..ab04b48 --- /dev/null +++ b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRelayReadType.java @@ -0,0 +1,9 @@ +package love.distributedrebirth.no2all.react.nostr.client; + +public enum NoStrRelayReadType { + + NONE, + ALL, + + ; +} diff --git a/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRtsClient.java b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRtsClient.java new file mode 100644 index 0000000..5c3354c --- /dev/null +++ b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/client/NoStrRtsClient.java @@ -0,0 +1,71 @@ +package love.distributedrebirth.no2all.react.nostr.client; + +import love.distributedrebirth.no2all.nostr.NoStr; +import love.distributedrebirth.no2all.nostr.fire.NoStrFirePipe; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelay; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientAuth; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientCount; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientEose; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientEvent; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientNotice; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientOk; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayAuth; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayClose; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayCount; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayEvent; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayReq; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactListener; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.react.wire.client.No2AllActClientClose; +import love.distributedrebirth.no2all.react.wire.client.No2AllActClientSendMessage; +import love.distributedrebirth.no2all.react.wire.client.No2AllArtClient; +import love.distributedrebirth.no2all.react.wire.client.No2AllArtClientOnMessage; +import love.distributedrebirth.no2all.react.wire.client.No2AllRtsClient; + +public class NoStrRtsClient implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(NoStrRtsClient.class); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.registrate(No2AllRtsClient.API, No2AllArtClient.class, artClient -> { + No2AllReactSlot clientSlot = artClient.getData().getSlot(); + react.claimIn(clientSlot, NoStrMsgToRelayAuth.class); + react.claimIn(clientSlot, NoStrMsgToRelayClose.class); + react.claimIn(clientSlot, NoStrMsgToRelayCount.class); + react.claimIn(clientSlot, NoStrMsgToRelayEvent.class); + react.claimIn(clientSlot, NoStrMsgToRelayReq.class); + react.claimOut(clientSlot, NoStrMsgToClientAuth.class); + react.claimOut(clientSlot, NoStrMsgToClientCount.class); + react.claimOut(clientSlot, NoStrMsgToClientEose.class); + react.claimOut(clientSlot, NoStrMsgToClientEvent.class); + react.claimOut(clientSlot, NoStrMsgToClientNotice.class); + react.claimOut(clientSlot, NoStrMsgToClientOk.class); + NoStrFirePipe pipeReader = NoStr.FACTORY.pipeReaderClient(reason -> { + react.fire(clientSlot, No2AllActClientClose.ofRefuse(reason)); + }, message -> { + react.fire(clientSlot, message); + }); + react.registrate(clientSlot, No2AllArtClientOnMessage.class, v -> { + pipeReader.onFire(v.getData().getMessage()); + }); + react.registrate(clientSlot, NoStrMsgToRelayAuth.class, pipeWriter(react, clientSlot)); + react.registrate(clientSlot, NoStrMsgToRelayClose.class, pipeWriter(react, clientSlot)); + react.registrate(clientSlot, NoStrMsgToRelayCount.class, pipeWriter(react, clientSlot)); + react.registrate(clientSlot, NoStrMsgToRelayEvent.class, pipeWriter(react, clientSlot)); + react.registrate(clientSlot, NoStrMsgToRelayReq.class, pipeWriter(react, clientSlot)); + }); + } + + private No2AllReactListener pipeWriter(No2AllReact react, No2AllReactSlot clientSlot) { + return v -> { + react.fire(clientSlot, new No2AllActClientSendMessage(v.getData().toBible().toString())); + }; + } +} diff --git a/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/NoStrRtsServer.java b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/NoStrRtsServer.java new file mode 100644 index 0000000..1fb9588 --- /dev/null +++ b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/NoStrRtsServer.java @@ -0,0 +1,86 @@ +package love.distributedrebirth.no2all.react.nostr.server; + +import love.distributedrebirth.no2all.nostr.NoStr; +import love.distributedrebirth.no2all.nostr.fire.NoStrFirePipe; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClient; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientAuth; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientCount; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientEose; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientEvent; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientNotice; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientOk; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayAuth; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayClose; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayCount; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayEvent; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayReq; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactListener; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServerBroadcastMessage; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServerSocketClose; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServerSocketSendMessage; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocket; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocketOnMessage; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServer; + +public class NoStrRtsServer implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(NoStrRtsServer.class); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.requireSlot(API, No2AllRtsServer.API); + react.claimIn(API, NoStrMsgToClientNotice.class); + react.registrate(API, NoStrMsgToClientNotice.class, v -> { + react.fireForEach(No2AllRtsServer.API, new No2AllActServerBroadcastMessage(v.getData().toBible().toString())); + }); + react.registrate(No2AllRtsServer.API, No2AllArtServer.class, artServer -> { + No2AllReactSlot serverSlot = artServer.getData().getSlot(); + react.claimIn(serverSlot, NoStrMsgToClientNotice.class); + react.registrate(serverSlot, No2AllArtServerSocket.class, artSocket -> { + No2AllReactSlot socketSlot = artSocket.getData().getSlot(); + react.claimIn(socketSlot, NoStrMsgToClientAuth.class); + react.claimIn(socketSlot, NoStrMsgToClientCount.class); + react.claimIn(socketSlot, NoStrMsgToClientEose.class); + react.claimIn(socketSlot, NoStrMsgToClientEvent.class); + react.claimIn(socketSlot, NoStrMsgToClientNotice.class); + react.claimIn(socketSlot, NoStrMsgToClientOk.class); + react.claimOut(socketSlot, NoStrMsgToRelayAuth.class); + react.claimOut(socketSlot, NoStrMsgToRelayClose.class); + react.claimOut(socketSlot, NoStrMsgToRelayCount.class); + react.claimOut(socketSlot, NoStrMsgToRelayEvent.class); + react.claimOut(socketSlot, NoStrMsgToRelayReq.class); + NoStrFirePipe pipeReader = NoStr.FACTORY.pipeReaderRelay(reason -> { + react.fire(socketSlot, No2AllActServerSocketClose.ofRefuse(reason)); + }, message -> { + react.fire(serverSlot, message); + }); + react.registrate(socketSlot, No2AllArtServerSocketOnMessage.class, v -> { + pipeReader.onFire(v.getData().getMessage()); + }); + react.registrate(socketSlot, NoStrMsgToClientAuth.class, pipeWriter(react, socketSlot)); + react.registrate(socketSlot, NoStrMsgToClientCount.class, pipeWriter(react, socketSlot)); + react.registrate(socketSlot, NoStrMsgToClientEose.class, pipeWriter(react, socketSlot)); + react.registrate(socketSlot, NoStrMsgToClientEvent.class, pipeWriter(react, socketSlot)); + react.registrate(socketSlot, NoStrMsgToClientNotice.class, pipeWriter(react, socketSlot)); + react.registrate(socketSlot, NoStrMsgToClientOk.class, pipeWriter(react, socketSlot)); + }); + react.registrate(serverSlot, NoStrMsgToClientNotice.class, v -> { + react.fire(serverSlot, new No2AllActServerBroadcastMessage(v.getData().toBible().toString())); + }); + }); + } + + private No2AllReactListener pipeWriter(No2AllReact react, No2AllReactSlot clientSlot) { + return v -> { + react.fire(clientSlot, new No2AllActServerSocketSendMessage(v.getData().toBible().toString())); + }; + } +} diff --git a/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrArtServerRelayStoreEvent.java b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrArtServerRelayStoreEvent.java new file mode 100644 index 0000000..edbc6a2 --- /dev/null +++ b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrArtServerRelayStoreEvent.java @@ -0,0 +1,22 @@ +package love.distributedrebirth.no2all.react.nostr.server.relay; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; + +public final class NoStrArtServerRelayStoreEvent { + + private final NoStrEvent event; + private final String remoteAddr; + + protected NoStrArtServerRelayStoreEvent(NoStrEvent event, String remoteAddr) { + this.event = event; + this.remoteAddr = remoteAddr; + } + + public NoStrEvent getEvent() { + return event; + } + + public String getRemoteAddr() { + return remoteAddr; + } +} diff --git a/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrRtsServerRelay.java b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrRtsServerRelay.java new file mode 100644 index 0000000..ab3bc9b --- /dev/null +++ b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrRtsServerRelay.java @@ -0,0 +1,61 @@ +package love.distributedrebirth.no2all.react.nostr.server.relay; + +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEventSignature; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToClientOk; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayEvent; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplMessageOkReason; +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.react.nostr.server.NoStrRtsServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocket; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServer; + +public class NoStrRtsServerRelay implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(NoStrRtsServerRelay.class); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.requireSlot(API, NoStrRtsServer.API); + react.registrate(No2AllRtsServer.API, No2AllArtServer.class, artServer -> { + No2AllReactSlot serverSlot = artServer.getData().getSlot(); + react.registrate(serverSlot, No2AllArtServerSocket.class, artSocket -> { + No2AllReactSlot socketSlot = artSocket.getData().getSlot(); + react.registrate(socketSlot, NoStrMsgToRelayEvent.class, v -> { + handleRelayEvent(react, v.getData().getEvent(), artSocket.getData().getRemoteAddr(), socketSlot); + }); + }); + }); + } + + private void handleRelayEvent(No2AllReact react, NoStrEvent event, String remoteAddr, No2AllReactSlot socketSlot) { + boolean eventSaved = false; + String eventResponse = ""; + NoStrImplMessageOkReason eventReason = NoStrImplMessageOkReason.DEFAULT; + + // filter on + event.getPayload().getKind(); + event.getPayload().getPublicKey(); + event.getPayload().getTags(); + + eventSaved = NoStrEventSignature.verify(event); + if (!eventSaved) { + eventReason = NoStrImplMessageOkReason.INVALID; + } + + System.out.println("Validate event: " + eventSaved + " eventId: " + event.getId()); + + + if (eventSaved) { + react.fire(API, new NoStrArtServerRelayStoreEvent(event, remoteAddr)); + } + react.fire(socketSlot, new NoStrMsgToClientOk(event.getId(), eventSaved, eventReason, eventResponse)); + } +} diff --git a/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrRtsServerRelaySubs.java b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrRtsServerRelaySubs.java new file mode 100644 index 0000000..c22a6d8 --- /dev/null +++ b/no2all-react-nostr/src/main/java/love/distributedrebirth/no2all/react/nostr/server/relay/NoStrRtsServerRelaySubs.java @@ -0,0 +1,39 @@ +package love.distributedrebirth.no2all.react.nostr.server.relay; + +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayClose; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayReq; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocket; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServer; + +public class NoStrRtsServerRelaySubs implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(NoStrRtsServerRelaySubs.class); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.requireSlot(API, NoStrRtsServerRelay.API); + react.registrate(No2AllRtsServer.API, No2AllArtServer.class, v -> { + No2AllReactSlot serverSlot = v.getData().getSlot(); + react.registrate(serverSlot, No2AllArtServerSocket.class, x -> { + No2AllReactSlot socketSlot = x.getData().getSlot(); + react.registrate(socketSlot, NoStrMsgToRelayReq.class, y -> { + String subId = y.getData().getSubscriptionId(); + y.getData().getFilters(); + System.out.println("sub new: " + subId); + }); + react.registrate(socketSlot, NoStrMsgToRelayClose.class, y -> { + String subId = y.getData().getSubscriptionId(); + System.out.println("sub clone: " + subId); + }); + }); + }); + } +} diff --git a/no2all-react-nostr/src/test/java/love/distributedrebirth/no2all/react/nostr/client/NoStrClientTest.java b/no2all-react-nostr/src/test/java/love/distributedrebirth/no2all/react/nostr/client/NoStrClientTest.java new file mode 100644 index 0000000..020a237 --- /dev/null +++ b/no2all-react-nostr/src/test/java/love/distributedrebirth/no2all/react/nostr/client/NoStrClientTest.java @@ -0,0 +1,110 @@ +package love.distributedrebirth.no2all.react.nostr.client; + +import java.net.URI; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import love.distributedrebirth.no2all.nostr.NoStr; +import love.distributedrebirth.no2all.nostr.fire.NoStrFireWaterBoiler; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentity; +import love.distributedrebirth.no2all.nostr.model.NoStrIdentityPrivateKey; +import love.distributedrebirth.no2all.nostr.model.event.NoStrEvent; +import love.distributedrebirth.no2all.nostr.model.message.NoStrMsgToRelayClose; +import love.distributedrebirth.no2all.nostr.nip.NoStrImplEventKind; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.warp.No2AllReactWarpCore; +import love.distributedrebirth.no2all.react.warp.No2AllRtsWarpSpeedMonitor; +import love.distributedrebirth.no2all.react.wire.client.No2AllActClient; +import love.distributedrebirth.no2all.react.wire.client.No2AllActClientClose; +import love.distributedrebirth.no2all.react.wire.client.No2AllActClientConnect; +import love.distributedrebirth.no2all.react.wire.client.No2AllArtClient; +import love.distributedrebirth.no2all.react.wire.client.No2AllArtClientOnClose; +import love.distributedrebirth.no2all.react.wire.client.No2AllArtClientOnError; +import love.distributedrebirth.no2all.react.wire.client.No2AllArtClientOnOpen; +import love.distributedrebirth.no2all.react.wire.client.No2AllRtsClient; +import love.distributedrebirth.no2all.wire.ojw.OjwWireClientEndpoint; + +public class NoStrClientTest { + + private final static NoStrIdentity SENDER = new NoStrIdentity(NoStrIdentityPrivateKey.ofRandom()); + // private final static Map RELAYS = Map.of("brb", "brb.io", + // "damus", "relay.damus.io", "ZBD", "nostr.zebedee.cloud", "taxi", + // "relay.taxi", "vision", "relay.nostr.vision"); + + public static void main(String[] args) throws Exception { + No2AllReactWarpCore react = new No2AllReactWarpCore(); + react.load(new No2AllRtsWarpSpeedMonitor()); + react.load(new No2AllRtsClient()); + react.load(new NoStrRtsClient()); + + react.registrate(No2AllRtsClient.API, No2AllArtClient.class, v -> { + No2AllReactSlot clientSlot = v.getData().getSlot(); + System.out.println("NoStrClient connect"); + react.registrate(clientSlot, No2AllArtClientOnError.class, x -> { + System.out.println("NoStrClient socket error: " + x.getData().getError().getMessage()); + }); + react.registrate(clientSlot, No2AllArtClientOnClose.class, x -> { + System.out.println("NoStrClient socket close"); + }); + react.registrate(clientSlot, No2AllArtClientOnOpen.class, x -> { + System.out.println("NoStrClient socket open"); + react.fire(clientSlot, new NoStrMsgToRelayClose("give-me-error")); + }); + react.fire(clientSlot, new No2AllActClientConnect()); + }); + + OjwWireClientEndpoint clientEndpoint = new OjwWireClientEndpoint(new URI("ws://localhost:8080")); + System.out.println("NoStrClient start"); + react.fire(No2AllRtsClient.API, new No2AllActClient(clientEndpoint)); + + OjwWireClientEndpoint clientEndpoint2 = new OjwWireClientEndpoint(new URI("ws://localhost:8080")); + System.out.println("NoStrClient start2"); + react.fire(No2AllRtsClient.API, new No2AllActClient(clientEndpoint2)); + + for (int i = 0; i < 100; i++) { + react.fire(No2AllRtsClient.API, SENDER); + Thread.sleep(100); + } + + Thread.sleep(500); + + Thread exitTimer = new Thread(() -> { + try { + Thread.sleep(2000); + } catch (InterruptedException e2) { + // TODO Auto-generated catch block + e2.printStackTrace(); + } + System.out.println("NoStrClient stopping"); + react.fireForEach(No2AllRtsClient.API, No2AllActClientClose.ofNormal("exit")); + System.out.println("NoStrClient stopped"); + }); + exitTimer.start(); + } + + public static void main2(String[] args) throws Exception { + + + + ExecutorService executor = Executors.newFixedThreadPool(10); + executor.submit(() -> { + try { + NoStrFireWaterBoiler userBoiler = NoStr.FACTORY.boiler(SENDER); + NoStrEvent event = userBoiler.smoker() + .tagSubject("Reply to all") + .tagR("") + .burn() + .water(NoStrImplEventKind.TEXT_NOTE, "Hello world"); + + System.out.println(event); + // GenericMessage message = new EventMessage(event); + // CLIENT.send(message); + } catch (Exception ex) { + ex.printStackTrace(); + } + }); + executor.shutdown(); + executor.awaitTermination(60, TimeUnit.SECONDS); + } +} diff --git a/no2all-react-nostr/src/test/java/love/distributedrebirth/no2all/react/nostr/server/NoStrServerTest.java b/no2all-react-nostr/src/test/java/love/distributedrebirth/no2all/react/nostr/server/NoStrServerTest.java new file mode 100644 index 0000000..17ff855 --- /dev/null +++ b/no2all-react-nostr/src/test/java/love/distributedrebirth/no2all/react/nostr/server/NoStrServerTest.java @@ -0,0 +1,65 @@ +package love.distributedrebirth.no2all.react.nostr.server; + +import java.net.InetSocketAddress; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.nostr.server.relay.NoStrRtsServerRelay; +import love.distributedrebirth.no2all.react.nostr.server.relay.NoStrRtsServerRelaySubs; +import love.distributedrebirth.no2all.react.warp.No2AllReactWarpCore; +import love.distributedrebirth.no2all.react.warp.No2AllRtsWarpSpeedMonitor; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerOnStart; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocket; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocketOnClose; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServerLog; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServer; +import love.distributedrebirth.no2all.wire.ojw.OjwWireServer; +import love.distributedrebirth.no2all.wire.ojw.OjwWireServerEndpoint; + +public class NoStrServerTest { + static OjwWireServer server = null; + public static void main(String[] args) throws Exception { + No2AllReactWarpCore react = new No2AllReactWarpCore(); + react.load(new No2AllRtsWarpSpeedMonitor()); + react.load(new No2AllRtsServer()); + react.load(new No2AllRtsServerLog()); + react.load(new NoStrRtsServer()); + react.load(new NoStrRtsServerRelay()); + react.load(new NoStrRtsServerRelaySubs()); + + react.registrate(No2AllRtsServer.API, No2AllArtServer.class, artServer -> { + No2AllReactSlot serverSlot = artServer.getData().getSlot(); + react.registrate(serverSlot, No2AllArtServerOnStart.class, v -> { + System.out.println("RelayServer started: " + v.getTarget().getSlotSlug()); + }); + react.registrate(serverSlot, No2AllArtServerSocket.class, v -> { + System.out.println("RelayServer client-open: " + v.getData().getSlot().getSlotPath()); + react.registrate(v.getData().getSlot(), No2AllArtServerSocketOnClose.class, y -> { + System.out.println("RelayServer client-close: " + y.getData().getCode() + " of: " + y.getTarget().getSlotPath()); + }); + }); + react.registrate(serverSlot, OjwWireServer.class, x -> { // TODO: add interface matching ... + server = x.getData(); + }); + }); + System.out.println("RelayServer fire configs"); + OjwWireServerEndpoint serverEndpoint = new OjwWireServerEndpoint(new InetSocketAddress(8080), 120); + react.fire(No2AllRtsServer.API, new No2AllActServer(serverEndpoint)); + + for (int i=0;i<1000;i++) { + //react.fire(No2AllServerRTS.API, react); + Thread.sleep(100); + } + + Thread.sleep(40000); + + if (server != null) { + System.out.println("RelayServers stopping"); + server.getHandler().stop(); + } + + //react.fireForEach(No2AllServerRTS.API, new No2AllActServerStop()); + System.out.println("RelayServers stopped"); + } +} diff --git a/no2all-react-wire/pom.xml b/no2all-react-wire/pom.xml new file mode 100644 index 0000000..62c9ad4 --- /dev/null +++ b/no2all-react-wire/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-react-wire + No2All-React-Wire + + + ${project.groupId} + no2all-react + + + ${project.groupId} + no2all-wire + + + \ No newline at end of file diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClient.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClient.java new file mode 100644 index 0000000..c1e7701 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClient.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.wire.client; + +import love.distributedrebirth.no2all.wire.WireClientEndpoint; + +public final class No2AllActClient { + + private final WireClientEndpoint endpoint; + + public No2AllActClient(WireClientEndpoint endpoint) { + this.endpoint = endpoint; + } + + public WireClientEndpoint getEndpoint() { + return endpoint; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientClose.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientClose.java new file mode 100644 index 0000000..d9a4cf5 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientClose.java @@ -0,0 +1,28 @@ +package love.distributedrebirth.no2all.react.wire.client; + +public final class No2AllActClientClose { + + private final int code; + private final String message; + + public No2AllActClientClose(int code, String message) { + this.code = code; + this.message = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public static No2AllActClientClose ofNormal(String message) { + return new No2AllActClientClose(1000, message); + } + + public static No2AllActClientClose ofRefuse(String message) { + return new No2AllActClientClose(1003, message); + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientConnect.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientConnect.java new file mode 100644 index 0000000..9047459 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientConnect.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.react.wire.client; + +public final class No2AllActClientConnect { + + public No2AllActClientConnect() { + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientSendBinary.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientSendBinary.java new file mode 100644 index 0000000..6baaefb --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientSendBinary.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.wire.client; + +import java.nio.ByteBuffer; + +public final class No2AllActClientSendBinary { + + private final ByteBuffer message; + + public No2AllActClientSendBinary(ByteBuffer message) { + this.message = message; + } + + public ByteBuffer getMessage() { + return message; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientSendMessage.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientSendMessage.java new file mode 100644 index 0000000..72628d4 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllActClientSendMessage.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.client; + +public final class No2AllActClientSendMessage { + + private final String message; + + public No2AllActClientSendMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClient.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClient.java new file mode 100644 index 0000000..0cc5786 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClient.java @@ -0,0 +1,11 @@ +package love.distributedrebirth.no2all.react.wire.client; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.warp.No2AllActAbstractSlot; + +public final class No2AllArtClient extends No2AllActAbstractSlot { + + protected No2AllArtClient(No2AllReactSlot slot) { + super(slot); + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnBinary.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnBinary.java new file mode 100644 index 0000000..382115f --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnBinary.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.wire.client; + +import java.nio.ByteBuffer; + +public final class No2AllArtClientOnBinary { + + private final ByteBuffer message; + + protected No2AllArtClientOnBinary(ByteBuffer message) { + this.message = message; + } + + public ByteBuffer getMessage() { + return message; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnClose.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnClose.java new file mode 100644 index 0000000..129fa9e --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnClose.java @@ -0,0 +1,26 @@ +package love.distributedrebirth.no2all.react.wire.client; + +public final class No2AllArtClientOnClose { + + private final int code; + private final String reason; + private final boolean remote; + + protected No2AllArtClientOnClose(int code, String reason, boolean remote) { + this.code = code; + this.reason = reason; + this.remote = remote; + } + + public int getCode() { + return code; + } + + public String getReason() { + return reason; + } + + public boolean isRemote() { + return remote; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnError.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnError.java new file mode 100644 index 0000000..fa47579 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnError.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.client; + +public final class No2AllArtClientOnError { + + private final Throwable error; + + protected No2AllArtClientOnError(Throwable error) { + this.error = error; + } + + public Throwable getError() { + return error; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnMessage.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnMessage.java new file mode 100644 index 0000000..7deeb1b --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnMessage.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.client; + +public final class No2AllArtClientOnMessage { + + private final String message; + + protected No2AllArtClientOnMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnOpen.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnOpen.java new file mode 100644 index 0000000..7bb6613 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnOpen.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.react.wire.client; + +public final class No2AllArtClientOnOpen { + + protected No2AllArtClientOnOpen() { + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnStart.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnStart.java new file mode 100644 index 0000000..f8010ac --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllArtClientOnStart.java @@ -0,0 +1,11 @@ +package love.distributedrebirth.no2all.react.wire.client; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.warp.No2AllActAbstractSlot; + +public final class No2AllArtClientOnStart extends No2AllActAbstractSlot { + + protected No2AllArtClientOnStart(No2AllReactSlot slot) { + super(slot); + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllRtsClient.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllRtsClient.java new file mode 100644 index 0000000..bf48aae --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/client/No2AllRtsClient.java @@ -0,0 +1,90 @@ +package love.distributedrebirth.no2all.react.wire.client; + +import java.nio.ByteBuffer; + +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientEndpoint; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class No2AllRtsClient implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(No2AllRtsClient.class); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.claimIn(API, No2AllActClient.class); + react.claimOut(API, No2AllArtClient.class); + react.registrate(API, No2AllActClient.class, actClient -> { + WireClientEndpoint clientEndpoint = actClient.getData().getEndpoint(); + No2AllReactSlot clientSlot = No2AllReactSlot.of(API, "wireId=" + clientEndpoint.wireId()); + WireClient client = clientEndpoint.wireClient(wireHandler(react, clientSlot)); + react.claim(clientSlot); + react.claimIn(clientSlot, No2AllActClientConnect.class); + react.claimIn(clientSlot, No2AllActClientClose.class); + react.claimIn(clientSlot, No2AllActClientSendMessage.class); + react.claimIn(clientSlot, No2AllActClientSendBinary.class); + react.claimOut(clientSlot, No2AllArtClientOnStart.class); + react.claimOut(clientSlot, No2AllArtClientOnOpen.class); + react.claimOut(clientSlot, No2AllArtClientOnClose.class); + react.claimOut(clientSlot, No2AllArtClientOnError.class); + react.claimOut(clientSlot, No2AllArtClientOnMessage.class); + react.claimOut(clientSlot, No2AllArtClientOnBinary.class); + react.claimOut(clientSlot, WireClient.class); + react.registrate(clientSlot, No2AllActClientConnect.class, v -> { + client.connect(); + }); + react.registrate(clientSlot, No2AllActClientClose.class, v -> { + client.close(v.getData().getCode(), v.getData().getMessage()); + }); + react.registrate(clientSlot, No2AllActClientSendMessage.class, v -> { + client.sendMessage(v.getData().getMessage()); + }); + react.registrate(clientSlot, No2AllActClientSendBinary.class, v -> { + client.sendBinary(v.getData().getMessage()); + }); + react.fire(API, new No2AllArtClient(clientSlot)); + react.fire(clientSlot, client); + }); + } + + private WireClientHandler wireHandler(No2AllReact react, No2AllReactSlot clientSlot) { + return new WireClientHandler() { + @Override + public void onStart() { + react.fire(clientSlot, new No2AllArtClientOnStart(clientSlot)); + } + + @Override + public void onOpen() { + react.fire(clientSlot, new No2AllArtClientOnOpen()); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + react.fire(clientSlot, new No2AllArtClientOnClose(code, reason, remote)); + } + + @Override + public void onError(Throwable error) { + react.fire(clientSlot, new No2AllArtClientOnError(error)); + } + + @Override + public void onMessage(String message) { + react.fire(clientSlot, new No2AllArtClientOnMessage(message)); + } + + @Override + public void onBinary(ByteBuffer message) { + react.fire(clientSlot, new No2AllArtClientOnBinary(message)); + } + }; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServer.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServer.java new file mode 100644 index 0000000..a1d952e --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServer.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import love.distributedrebirth.no2all.wire.WireServerEndpoint; + +public final class No2AllActServer { + + private final WireServerEndpoint endpoint; + + public No2AllActServer(WireServerEndpoint endpoint) { + this.endpoint = endpoint; + } + + public WireServerEndpoint getEndpoint() { + return endpoint; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerBroadcastBinary.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerBroadcastBinary.java new file mode 100644 index 0000000..8a29be6 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerBroadcastBinary.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import java.nio.ByteBuffer; + +public final class No2AllActServerBroadcastBinary { + + private final ByteBuffer message; + + public No2AllActServerBroadcastBinary(ByteBuffer message) { + this.message = message; + } + + public ByteBuffer getMessage() { + return message; + } +} \ No newline at end of file diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerBroadcastMessage.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerBroadcastMessage.java new file mode 100644 index 0000000..fcbe07a --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerBroadcastMessage.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllActServerBroadcastMessage { + + private final String message; + + public No2AllActServerBroadcastMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} \ No newline at end of file diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketClose.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketClose.java new file mode 100644 index 0000000..24312f0 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketClose.java @@ -0,0 +1,28 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllActServerSocketClose { + + private final int code; + private final String messag; + + public No2AllActServerSocketClose(int code, String message) { + this.code = code; + this.messag = message; + } + + public int getCode() { + return code; + } + + public String getMessage() { + return messag; + } + + public static No2AllActServerSocketClose ofNormal(String message) { + return new No2AllActServerSocketClose(1000, message); + } + + public static No2AllActServerSocketClose ofRefuse(String message) { + return new No2AllActServerSocketClose(1003, message); + } +} \ No newline at end of file diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketSendBinary.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketSendBinary.java new file mode 100644 index 0000000..01a656e --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketSendBinary.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import java.nio.ByteBuffer; + +public final class No2AllActServerSocketSendBinary { + + private final ByteBuffer message; + + public No2AllActServerSocketSendBinary(ByteBuffer message) { + this.message = message; + } + + public ByteBuffer getMessage() { + return message; + } +} \ No newline at end of file diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketSendMessage.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketSendMessage.java new file mode 100644 index 0000000..b147ce0 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllActServerSocketSendMessage.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllActServerSocketSendMessage { + + private final String message; + + public No2AllActServerSocketSendMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} \ No newline at end of file diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServer.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServer.java new file mode 100644 index 0000000..0176e7f --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServer.java @@ -0,0 +1,11 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.warp.No2AllActAbstractSlot; + +public final class No2AllArtServer extends No2AllActAbstractSlot { + + protected No2AllArtServer(No2AllReactSlot slot) { + super(slot); + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerOnError.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerOnError.java new file mode 100644 index 0000000..b81751a --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerOnError.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllArtServerOnError { + + private final Throwable error; + + protected No2AllArtServerOnError(Throwable error) { + this.error = error; + } + + public Throwable getError() { + return error; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerOnStart.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerOnStart.java new file mode 100644 index 0000000..0a69368 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerOnStart.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllArtServerOnStart { + + protected No2AllArtServerOnStart() { + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocket.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocket.java new file mode 100644 index 0000000..793f8a7 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocket.java @@ -0,0 +1,18 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.warp.No2AllActAbstractSlot; + +public final class No2AllArtServerSocket extends No2AllActAbstractSlot { + + private final String remoteAddr; + + protected No2AllArtServerSocket(No2AllReactSlot slot, String remoteAddr) { + super(slot); + this.remoteAddr = remoteAddr; + } + + public String getRemoteAddr() { + return remoteAddr; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnBinary.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnBinary.java new file mode 100644 index 0000000..8be8e6e --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnBinary.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import java.nio.ByteBuffer; + +public final class No2AllArtServerSocketOnBinary { + + private final ByteBuffer message; + + protected No2AllArtServerSocketOnBinary(ByteBuffer message) { + this.message = message; + } + + public ByteBuffer getMessage() { + return message; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnClose.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnClose.java new file mode 100644 index 0000000..dc74bcd --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnClose.java @@ -0,0 +1,26 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllArtServerSocketOnClose { + + private final int code; + private final String reason; + private final boolean remote; + + protected No2AllArtServerSocketOnClose(int code, String reason, boolean remote) { + this.code = code; + this.reason = reason; + this.remote = remote; + } + + public int getCode() { + return code; + } + + public String getReason() { + return reason; + } + + public boolean isRemote() { + return remote; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnError.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnError.java new file mode 100644 index 0000000..1c5ee6a --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnError.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllArtServerSocketOnError { + + private final Throwable error; + + protected No2AllArtServerSocketOnError(Throwable error) { + this.error = error; + } + + public Throwable getError() { + return error; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnMessage.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnMessage.java new file mode 100644 index 0000000..d123dbb --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllArtServerSocketOnMessage.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.react.wire.server; + +public final class No2AllArtServerSocketOnMessage { + + private final String message; + + protected No2AllArtServerSocketOnMessage(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllRtsServer.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllRtsServer.java new file mode 100644 index 0000000..3c548f6 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllRtsServer.java @@ -0,0 +1,108 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import java.nio.ByteBuffer; + +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.wire.WireServer; +import love.distributedrebirth.no2all.wire.WireServerEndpoint; +import love.distributedrebirth.no2all.wire.WireServerHandler; +import love.distributedrebirth.no2all.wire.WireServerSocket; + +public class No2AllRtsServer implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(No2AllRtsServer.class); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.claimIn(API, No2AllActServer.class); + react.claimOut(API, No2AllArtServer.class); + react.registrate(API, No2AllActServer.class, actServer -> { + WireServerEndpoint serverEndpoint = actServer.getData().getEndpoint(); + No2AllReactSlot serverSlot = No2AllReactSlot.of(API, "wireId=" + serverEndpoint.wireId()); + WireServer server = serverEndpoint.wireServer(wireHandler(react, serverSlot)); + react.claim(serverSlot); + react.claimIn(serverSlot, No2AllActServerBroadcastMessage.class); + react.claimIn(serverSlot, No2AllActServerBroadcastBinary.class); + react.claimOut(serverSlot, No2AllArtServerOnStart.class); + react.claimOut(serverSlot, No2AllArtServerSocket.class); + react.claimOut(serverSlot, No2AllArtServerOnError.class); + react.claimOut(serverSlot, WireServer.class); + react.registrate(serverSlot, No2AllActServerBroadcastMessage.class, v -> { + server.broadcastMessage(v.getData().getMessage()); + }); + react.registrate(serverSlot, No2AllActServerBroadcastBinary.class, v -> { + server.broadcastBinary(v.getData().getMessage()); + }); + react.fire(API, new No2AllArtServer(serverSlot)); + react.fire(serverSlot, server); + }); + } + + private WireServerHandler wireHandler(No2AllReact react, No2AllReactSlot serverSlot) { + return new WireServerHandler() { + private No2AllReactSlot socketSlug(WireServerSocket conn) { + return No2AllReactSlot.of(serverSlot, "hashCode=" + conn.hashCode()); + } + + @Override + public void onStart() { + react.fire(serverSlot, new No2AllArtServerOnStart()); + } + + @Override + public void onOpen(WireServerSocket conn) { + No2AllReactSlot socketSlot = socketSlug(conn); + react.claim(socketSlot); + react.claimIn(socketSlot, No2AllActServerSocketSendMessage.class); + react.claimIn(socketSlot, No2AllActServerSocketSendBinary.class); + react.claimIn(socketSlot, No2AllActServerSocketClose.class); + react.claimOut(socketSlot, No2AllArtServerSocketOnClose.class); + react.claimOut(socketSlot, No2AllArtServerSocketOnError.class); + react.claimOut(socketSlot, No2AllArtServerSocketOnMessage.class); + react.claimOut(socketSlot, No2AllArtServerSocketOnBinary.class); + react.registrate(socketSlot, No2AllActServerSocketSendMessage.class, v -> { + conn.sendMessage(v.getData().getMessage()); + }); + react.registrate(socketSlot, No2AllActServerSocketSendBinary.class, v -> { + conn.sendBinary(v.getData().getMessage()); + }); + react.registrate(socketSlot, No2AllActServerSocketClose.class, v -> { + conn.close(v.getData().getCode(), v.getData().getMessage()); + }); + react.fire(serverSlot, new No2AllArtServerSocket(socketSlot, conn.getRemoteAddress())); + } + + @Override + public void onClose(WireServerSocket conn, int code, String reason, boolean remote) { + No2AllReactSlot socketSlot = socketSlug(conn); + react.fire(socketSlot, new No2AllArtServerSocketOnClose(code, reason, remote)); + react.release(socketSlot); + } + + @Override + public void onError(WireServerSocket conn, Throwable error) { + if (conn != null) { + react.fire(socketSlug(conn), new No2AllArtServerSocketOnError(error)); + } else { + react.fire(serverSlot, new No2AllArtServerOnError(error)); + } + } + + @Override + public void onMessage(WireServerSocket conn, String message) { + react.fire(socketSlug(conn), new No2AllArtServerSocketOnMessage(message)); + } + + @Override + public void onBinary(WireServerSocket conn, ByteBuffer message) { + react.fire(socketSlug(conn), new No2AllArtServerSocketOnBinary(message)); + } + }; + } +} diff --git a/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllRtsServerLog.java b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllRtsServerLog.java new file mode 100644 index 0000000..d0c3d46 --- /dev/null +++ b/no2all-react-wire/src/main/java/love/distributedrebirth/no2all/react/wire/server/No2AllRtsServerLog.java @@ -0,0 +1,34 @@ +package love.distributedrebirth.no2all.react.wire.server; + +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; + +public class No2AllRtsServerLog implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(No2AllRtsServerLog.class); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.registrate(No2AllRtsServer.API, No2AllArtServer.class, artServer -> { + No2AllReactSlot serverSlot = artServer.getData().getSlot(); + react.registrate(serverSlot, No2AllActServerBroadcastMessage.class, v -> { + System.out.println("No2AllRtsServerLog.broadcastMessage: " + v.getData().getMessage()); + }); + react.registrate(serverSlot, No2AllArtServerSocket.class, v -> { + No2AllReactSlot socketSlot = v.getData().getSlot(); + String wireId = socketSlot.getSlotSlug(); + react.registrate(socketSlot, No2AllArtServerSocketOnMessage.class, z -> { + System.out.println("No2AllRtsServerLog."+wireId+".socketOnMessage: " + z.getData().getMessage()); + }); + react.registrate(socketSlot, No2AllActServerSocketSendMessage.class, z -> { + System.out.println("No2AllRtsServerLog."+wireId+".socketSendMessage: " + z.getData().getMessage()); + }); + }); + }); + } +} diff --git a/no2all-react/pom.xml b/no2all-react/pom.xml new file mode 100644 index 0000000..feda2a5 --- /dev/null +++ b/no2all-react/pom.xml @@ -0,0 +1,10 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-react + No2All-React + \ No newline at end of file diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReact.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReact.java new file mode 100644 index 0000000..f75807a --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReact.java @@ -0,0 +1,17 @@ +package love.distributedrebirth.no2all.react; + +public interface No2AllReact { + + void claim(No2AllReactSlot slot); + void claimIn(No2AllReactSlot slot, Class eventType); + void claimOut(No2AllReactSlot slot, Class eventType); + void requireSlot(No2AllReactSlot slot, No2AllReactSlot dep); + void requireService(No2AllReactSlot slot, Class serviceType); + + void fire(No2AllReactSlot slot, Object event); + void fireForEach(No2AllReactSlot slot, Object event); + void registrate(No2AllReactSlot slot, Class eventType, No2AllReactListener listener); + + T service(Class serviceType); + void release(No2AllReactSlot slot); +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactListener.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactListener.java new file mode 100644 index 0000000..dedcf5f --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactListener.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.react; + +@FunctionalInterface +public interface No2AllReactListener { + + void onEvent(No2AllReactSlotSignal signal); +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlot.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlot.java new file mode 100644 index 0000000..d06b20b --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlot.java @@ -0,0 +1,75 @@ +package love.distributedrebirth.no2all.react; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public final class No2AllReactSlot { + + private final No2AllReactSlot parent; + private final String slotSlug; + private final String slotPath; + + private No2AllReactSlot(No2AllReactSlot parent, String slotSlug) { + this.parent = parent; + this.slotSlug = slotSlug; + this.slotPath = createSlotPath(); + } + + public No2AllReactSlot getParent() { + return parent; + } + + public String getSlotSlug() { + return slotSlug; + } + + public String getSlotPath() { + return slotPath; + } + + public String createSlotPath() { + List slugs = new ArrayList<>(); + No2AllReactSlot parentNode = this; + while (parentNode != null) { + slugs.add(parentNode.getSlotSlug()); + parentNode = parentNode.getParent(); + } + StringBuilder buf = new StringBuilder(); + for (int i = slugs.size() - 1; i >= 0; i--) { + buf.append(slugs.get(i)); + if (i > 0) { + buf.append(','); + } + } + return buf.toString(); + } + + @Override + public int hashCode() { + return Objects.hash(slotPath); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + No2AllReactSlot other = (No2AllReactSlot) obj; + return Objects.equals(slotPath, other.slotPath); + } + + public static No2AllReactSlot ofClass(Class scriptClz) { + return new No2AllReactSlot(null, scriptClz.getPackageName() + ":type=" + scriptClz.getSimpleName()); + } + + public static No2AllReactSlot of(No2AllReactSlot parent, String kvPair) { + return new No2AllReactSlot(parent, kvPair); + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlotLoad.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlotLoad.java new file mode 100644 index 0000000..a3dd090 --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlotLoad.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.react; + +public final class No2AllReactSlotLoad { + + public No2AllReactSlotLoad() { + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlotSignal.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlotSignal.java new file mode 100644 index 0000000..aeccabf --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactSlotSignal.java @@ -0,0 +1,30 @@ +package love.distributedrebirth.no2all.react; + +public final class No2AllReactSlotSignal { + + private final No2AllReactSlot target; + private final T data; + private final No2AllReact react; + + public No2AllReactSlotSignal(No2AllReactSlot target, T data, No2AllReact react) { + this.target = target; + this.data = data; + this.react = react; + } + + public No2AllReactSlot getTarget() { + return target; + } + + public T getData() { + return data; + } + + public No2AllReact getReact() { + return react; + } + +// public T $() { +// return getData(); +// } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactTypeScript.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactTypeScript.java new file mode 100644 index 0000000..55ac95f --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/No2AllReactTypeScript.java @@ -0,0 +1,7 @@ +package love.distributedrebirth.no2all.react; + +public interface No2AllReactTypeScript extends No2AllReactListener { + + default void onRelease(No2AllReactSlotSignal signal) { + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllActAbstractSlot.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllActAbstractSlot.java new file mode 100644 index 0000000..846f776 --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllActAbstractSlot.java @@ -0,0 +1,16 @@ +package love.distributedrebirth.no2all.react.warp; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; + +public abstract class No2AllActAbstractSlot { + + private final No2AllReactSlot slot; + + public No2AllActAbstractSlot(No2AllReactSlot slot) { + this.slot = slot; + } + + public final No2AllReactSlot getSlot() { + return slot; + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllArtWarpSlotAdd.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllArtWarpSlotAdd.java new file mode 100644 index 0000000..5e61bd4 --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllArtWarpSlotAdd.java @@ -0,0 +1,10 @@ +package love.distributedrebirth.no2all.react.warp; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; + +public final class No2AllArtWarpSlotAdd extends No2AllActAbstractSlot { + + protected No2AllArtWarpSlotAdd(No2AllReactSlot slot) { + super(slot); + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllArtWarpSlotRemove.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllArtWarpSlotRemove.java new file mode 100644 index 0000000..edf2ba2 --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllArtWarpSlotRemove.java @@ -0,0 +1,10 @@ +package love.distributedrebirth.no2all.react.warp; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; + +public final class No2AllArtWarpSlotRemove extends No2AllActAbstractSlot { + + protected No2AllArtWarpSlotRemove(No2AllReactSlot slot) { + super(slot); + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactSlotContract.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactSlotContract.java new file mode 100644 index 0000000..b800bb0 --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactSlotContract.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.react.warp; + +import java.util.ArrayList; +import java.util.List; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; + +public class No2AllReactSlotContract { + + private final No2AllReactSlot slot; + final List> slotTypesIn = new ArrayList<>(); + final List> slotTypesOut = new ArrayList<>(); + + public No2AllReactSlotContract(No2AllReactSlot slot) { + this.slot = slot; + } + + public No2AllReactSlot getSlot() { + return slot; + } + + public List> getSlotTypesIn() { + return slotTypesIn; + } + + public List> getSlotTypesOut() { + return slotTypesOut; + } +} \ No newline at end of file diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactWarpCore.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactWarpCore.java new file mode 100644 index 0000000..9428708 --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactWarpCore.java @@ -0,0 +1,184 @@ +package love.distributedrebirth.no2all.react.warp; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactListener; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; + +public class No2AllReactWarpCore implements No2AllReact { + + public static final No2AllReactSlot DILITHIUM = No2AllReactSlot.ofClass(No2AllReactWarpCore.class); + private final Map slots = new HashMap<>(); + private final Map, Object> services = new HashMap<>(); + private final List backlog = new ArrayList<>(); + private Object currentEvent = null; + + public No2AllReactWarpCore() { + claim(DILITHIUM); + toWarpFuel(DILITHIUM).registrateTypeIn(No2AllReactSlotLoad.class); + toWarpFuel(DILITHIUM).registrateTypeOut(No2AllArtWarpSlotAdd.class); + toWarpFuel(DILITHIUM).registrateTypeOut(No2AllArtWarpSlotRemove.class); + } + + public void load(No2AllReactTypeScript script) { + Objects.requireNonNull(script).onEvent(new No2AllReactSlotSignal<>(DILITHIUM, new No2AllReactSlotLoad(), this)); + } + + public No2AllReactSlotContract getSlotContract(No2AllReactSlot slot) { + return toWarpFuel(slot).readContract(); + } + + public List listChilds(No2AllReactSlot slot) { + Objects.requireNonNull(slot); + List result = new ArrayList<>(); + synchronized (slots) { + for (No2AllReactSlot slug : slots.keySet()) { + if (slot == null) { + result.add(slug); + continue; + } + if (slug.getParent() == null) { + continue; + } + if (slug.getParent().getSlotPath().equals(slot.getSlotPath())) { + result.add(slug); + } + } + } + return result; + } + + @Override + public void fireForEach(No2AllReactSlot slot, Object event) { + for (No2AllReactSlot target : listChilds(slot)) { + fire(target, event); + } + } + + @Override + public void fire(No2AllReactSlot slot, Object event) { + Objects.requireNonNull(slot); + Objects.requireNonNull(event); + if (currentEvent != null) { + backlog.add(new BacklogEvent(slot, event)); + return; + } + currentEvent = event; + try { + toWarpFuel(slot).fire(event, slot); + + List backlog2 = new ArrayList<>(backlog); + backlog.clear(); + for (BacklogEvent next : backlog2) { + toWarpFuel(next.slot).fire(next.event, next.slot); + } + } finally { + currentEvent = null; + } + } + + static class BacklogEvent { + No2AllReactSlot slot; + Object event; + public BacklogEvent(No2AllReactSlot slot, Object event) { + this.slot = slot; + this.event = event; + } + } + + @SuppressWarnings("unchecked") + @Override + public void registrate(No2AllReactSlot slot, Class eventType, No2AllReactListener listener) { + No2AllReactListener listenerObj = (No2AllReactListener) listener; + toWarpFuel(slot).registrateListener(eventType, listenerObj); + } + + @Override + public void release(No2AllReactSlot slot) { + toWarpFuel(slot).removeAll(); + synchronized (slots) { + slots.remove(slot); + } + fire(DILITHIUM, new No2AllArtWarpSlotRemove(slot)); + } + + @Override + public void claim(No2AllReactSlot slot) { + Objects.requireNonNull(slot); + synchronized (slots) { + if (slots.keySet().contains(slot)) { + throw new IllegalStateException("Slug already claimed: " + slot.getSlotPath()); + } + slots.put(slot, new No2AllReactWarpFuel(this, slot)); + } + fire(DILITHIUM, new No2AllArtWarpSlotAdd(slot)); + } + + @Override + public void claimOut(No2AllReactSlot slot, Class eventType) { + toWarpFuel(slot).registrateTypeOut(eventType); + } + + + @Override + public void claimIn(No2AllReactSlot slot, Class eventType) { + toWarpFuel(slot).registrateTypeIn(eventType); + } + + @Override + public void requireSlot(No2AllReactSlot slot, No2AllReactSlot dep) { + if (!isClaimed(dep)) { + throw new IllegalStateException("Script dependency missing: " + dep); + } + toWarpFuel(slot).addRequireSlot(dep); + } + + @Override + public void requireService(No2AllReactSlot slot, Class serviceType) { + toWarpFuel(slot).addRequireService(serviceType); + } + + @SuppressWarnings("unchecked") + @Override + public T service(Class serviceType) { + Objects.requireNonNull(serviceType); + synchronized (services) { + return (T) services.get(serviceType); + } + } + + public void addService(Class serviceType, T service) { + Objects.requireNonNull(serviceType); + Objects.requireNonNull(service); + synchronized (services) { + services.put(serviceType, service); + } + } + + public boolean isClaimed(No2AllReactSlot slug) { + Objects.requireNonNull(slug); + synchronized (slots) { + return slots.keySet().contains(slug); + } + } + + protected No2AllReactWarpFuel toWarpFuel(No2AllReactSlot slot) { + Objects.requireNonNull(slot); + No2AllReactWarpFuel result; + synchronized (slots) { + result = slots.get(slot); + if (result == null) { + throw new IllegalStateException("Slot not claimed: " + slot.getSlotPath()); + } + } + return result; + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactWarpFuel.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactWarpFuel.java new file mode 100644 index 0000000..2104b9f --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllReactWarpFuel.java @@ -0,0 +1,120 @@ +package love.distributedrebirth.no2all.react.warp; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import love.distributedrebirth.no2all.react.No2AllReactListener; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; + +public class No2AllReactWarpFuel { + + private final No2AllReactWarpCore no2AllReactWarpCore; + private final No2AllReactSlot containerSlot; + private final Map, List>> listeners = new HashMap<>(); + private final Set> claimTypesOut = new HashSet<>(); + private final Set> claimTypesIn = new HashSet<>(); + private final Set> requireServices = new HashSet<>(); + private final Set requireSlots = new HashSet<>(); + + protected No2AllReactWarpFuel(No2AllReactWarpCore no2AllReactWarpCore, No2AllReactSlot containerSlot) { + this.no2AllReactWarpCore = Objects.requireNonNull(no2AllReactWarpCore); + this.containerSlot = Objects.requireNonNull(containerSlot); + } + + private List> toViewOfListeners(Class eventType) { + List> result = new ArrayList<>(); + synchronized (listeners) { + List> typeListeners = listeners.get(eventType); + if (typeListeners == null) { + return result; + } + result.addAll(typeListeners); + } + return result; + } + + public void fire(Object event, No2AllReactSlot slot) { + for (No2AllReactListener listener : toViewOfListeners(event.getClass())) { + listener.onEvent(new No2AllReactSlotSignal<>(slot, event, this.no2AllReactWarpCore)); + } + } + + public void registrateListener(Class eventType, No2AllReactListener listener) { + Objects.requireNonNull(eventType); + Objects.requireNonNull(listener); + synchronized (listeners) { + List> typeListeners = listeners.get(eventType); + if (typeListeners == null) { + typeListeners = new ArrayList<>(); + listeners.put(eventType, typeListeners); + } + typeListeners.add(listener); + } + } + + public void registrateTypeOut(Class eventType) { + Objects.requireNonNull(eventType); + synchronized (claimTypesOut) { + claimTypesOut.add(eventType); + } + } + + public void registrateTypeIn(Class eventType) { + Objects.requireNonNull(eventType); + synchronized (claimTypesIn) { + claimTypesIn.add(eventType); + } + } + + public No2AllReactSlotContract readContract() { + No2AllReactSlotContract result = new No2AllReactSlotContract(containerSlot); + synchronized (claimTypesIn) { + result.slotTypesIn.addAll(claimTypesIn); + } + synchronized (claimTypesOut) { + result.slotTypesOut.addAll(claimTypesOut); + } + return result; + } + + public void addRequireService(Class serviceType) { + Objects.requireNonNull(serviceType); + synchronized (requireServices) { + requireServices.add(serviceType); + } + } + + public void addRequireSlot(No2AllReactSlot dep) { + Objects.requireNonNull(dep); + synchronized (requireSlots) { + requireSlots.add(dep); + } + } + + public void removeAll() { + synchronized (listeners) { + for (Class eventType : listeners.keySet()) { + List> typeListeners = listeners.get(eventType); + typeListeners.clear(); + } + } + synchronized (claimTypesOut) { + claimTypesOut.clear(); + } + synchronized (claimTypesIn) { + claimTypesIn.clear(); + } + synchronized (requireServices) { + requireServices.clear(); + } + synchronized (requireSlots) { + requireSlots.clear(); + } + } +} diff --git a/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllRtsWarpSpeedMonitor.java b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllRtsWarpSpeedMonitor.java new file mode 100644 index 0000000..7b886e6 --- /dev/null +++ b/no2all-react/src/main/java/love/distributedrebirth/no2all/react/warp/No2AllRtsWarpSpeedMonitor.java @@ -0,0 +1,30 @@ +package love.distributedrebirth.no2all.react.warp; + +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; + +import java.util.logging.Logger; + +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; + +public class No2AllRtsWarpSpeedMonitor implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(No2AllRtsWarpSpeedMonitor.class); + private static final No2AllReactSlot API_DILITHIUM = No2AllReactWarpCore.DILITHIUM; + private static final Logger LOG = Logger.getLogger(No2AllRtsWarpSpeedMonitor.class.getName()); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + No2AllReact react = signal.getReact(); + react.claim(API); + react.requireSlot(API, API_DILITHIUM); + react.registrate(API_DILITHIUM, No2AllArtWarpSlotAdd.class, v -> { + LOG.fine("slot-add: " + v.getData().getSlot().getSlotPath()); + }); + react.registrate(API_DILITHIUM, No2AllArtWarpSlotRemove.class, v -> { + LOG.fine("slot-remove: " + v.getData().getSlot().getSlotPath()); + }); + } +} diff --git a/no2all-wire-jetty/pom.xml b/no2all-wire-jetty/pom.xml new file mode 100644 index 0000000..dec9896 --- /dev/null +++ b/no2all-wire-jetty/pom.xml @@ -0,0 +1,38 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-wire-jetty + No2All-Wire-Org-Java-Websocket + + + ${project.groupId} + no2all-wire + + + ${project.groupId} + no2all-react-wire + test + + + org.eclipse.jetty.websocket + websocket-jetty-api + + + org.eclipse.jetty.websocket + websocket-jetty-server + + + org.eclipse.jetty.websocket + websocket-jetty-client + + + org.eclipse.jetty + jetty-slf4j-impl + test + + + \ No newline at end of file diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClient.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClient.java new file mode 100644 index 0000000..3105e92 --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClient.java @@ -0,0 +1,70 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.io.IOException; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.concurrent.ExecutionException; + +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.client.impl.JettyClientUpgradeRequest; + +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class JettyWireClient implements WireClient { + + private final URI uri; + private final WebSocketClient client; + private final JettyWireClientHandler handler; + + protected JettyWireClient(URI uri, WebSocketClient client, WireClientHandler handler) { + this.uri = uri; + this.client = client; + this.handler = new JettyWireClientHandler(handler); + } + + public JettyWireClientHandler getHandler() { + return handler; + } + + @Override + public void connect() { + try { + client.connect(handler, uri).get(); + } catch (IOException | InterruptedException | ExecutionException e) { + throw new IllegalStateException(e); + } + } + + @Override + public void close(int code, String message) { + client.getOpenSessions().forEach(v -> { + JettyClientUpgradeRequest req = JettyClientUpgradeRequest.class.cast(v.getUpgradeRequest()); + if (req.getURI().equals(uri)) { + v.close(code, message); + } + }); + } + + @Override + public void sendMessage(String message) { + client.getOpenSessions().forEach(v -> { + try { + v.getRemote().sendString(message); + } catch (IOException e) { + throw new IllegalStateException(e); + } + }); + } + + @Override + public void sendBinary(ByteBuffer message) { + client.getOpenSessions().forEach(v -> { + try { + v.getRemote().sendBytes(message); + } catch (IOException e) { + throw new IllegalStateException(e); + } + }); + } +} diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClientEndpoint.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClientEndpoint.java new file mode 100644 index 0000000..a0ca71f --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClientEndpoint.java @@ -0,0 +1,30 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.net.URI; + +import org.eclipse.jetty.websocket.client.WebSocketClient; + +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientEndpoint; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class JettyWireClientEndpoint implements WireClientEndpoint { + + private final URI uri; + private final WebSocketClient client; + + public JettyWireClientEndpoint(URI uri, WebSocketClient client) { + this.uri = uri; + this.client = client; + } + + @Override + public String wireId() { + return uri.toString(); + } + + @Override + public WireClient wireClient(WireClientHandler handler) { + return new JettyWireClient(uri, client, handler); + } +} diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClientHandler.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClientHandler.java new file mode 100644 index 0000000..4414efe --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireClientHandler.java @@ -0,0 +1,43 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.nio.ByteBuffer; + +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketListener; + +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class JettyWireClientHandler implements WebSocketListener { + + private final WireClientHandler handler; + + protected JettyWireClientHandler(WireClientHandler handler) { + this.handler = handler; + this.handler.onStart(); + } + + @Override + public void onWebSocketConnect(Session session) { + handler.onOpen(); + } + + @Override + public void onWebSocketClose(int statusCode, String reason) { + handler.onClose(statusCode, reason, false); + } + + @Override + public void onWebSocketError(Throwable cause) { + handler.onError(cause); + } + + @Override + public void onWebSocketBinary(byte[] payload, int offset, int len) { + handler.onBinary(ByteBuffer.wrap(payload, offset, len)); + } + + @Override + public void onWebSocketText(String message) { + handler.onMessage(message); + } +} diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServer.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServer.java new file mode 100644 index 0000000..27950b4 --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServer.java @@ -0,0 +1,41 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; + +import love.distributedrebirth.no2all.wire.WireServer; +import love.distributedrebirth.no2all.wire.WireServerHandler; + +public class JettyWireServer implements WireServer { + + private final JettyWebSocketServerContainer wsContainer; + + protected JettyWireServer(JettyWebSocketServerContainer wsContainer, String servletPath, WireServerHandler handler) { + this.wsContainer = wsContainer; + this.wsContainer.addMapping(servletPath, new JettyWireServerEndpointCreator(handler)); + } + + @Override + public void broadcastMessage(String message) { + wsContainer.getOpenSessions().forEach(v -> { + try { + v.getRemote().sendString(message); + } catch (IOException e) { + throw new IllegalStateException(e); + } + }); + } + + @Override + public void broadcastBinary(ByteBuffer message) { + wsContainer.getOpenSessions().forEach(v -> { + try { + v.getRemote().sendBytes(message); + } catch (IOException e) { + throw new IllegalStateException(e); + } + }); + } +} diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerEndpoint.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerEndpoint.java new file mode 100644 index 0000000..595d32c --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerEndpoint.java @@ -0,0 +1,28 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; + +import love.distributedrebirth.no2all.wire.WireServer; +import love.distributedrebirth.no2all.wire.WireServerEndpoint; +import love.distributedrebirth.no2all.wire.WireServerHandler; + +public class JettyWireServerEndpoint implements WireServerEndpoint { + + private final JettyWebSocketServerContainer wsContainer; + private final String servletPath; + + public JettyWireServerEndpoint(JettyWebSocketServerContainer wsContainer, String servletPath) { + this.wsContainer = wsContainer; + this.servletPath = servletPath; + } + + @Override + public String wireId() { + return servletPath; + } + + @Override + public WireServer wireServer(WireServerHandler handler) { + return new JettyWireServer(wsContainer, servletPath, handler); + } +} diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerEndpointCreator.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerEndpointCreator.java new file mode 100644 index 0000000..2e9b14c --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerEndpointCreator.java @@ -0,0 +1,22 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import org.eclipse.jetty.websocket.server.JettyServerUpgradeRequest; +import org.eclipse.jetty.websocket.server.JettyServerUpgradeResponse; +import org.eclipse.jetty.websocket.server.JettyWebSocketCreator; + +import love.distributedrebirth.no2all.wire.WireServerHandler; + +public class JettyWireServerEndpointCreator implements JettyWebSocketCreator { + + private final WireServerHandler handler; + + public JettyWireServerEndpointCreator(WireServerHandler handler) { + this.handler = handler; + } + + @Override + public Object createWebSocket(JettyServerUpgradeRequest jettyServerUpgradeRequest, + JettyServerUpgradeResponse jettyServerUpgradeResponse) { + return new JettyWireServerHandler(handler); + } +} diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerHandler.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerHandler.java new file mode 100644 index 0000000..7103963 --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerHandler.java @@ -0,0 +1,45 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.nio.ByteBuffer; + +import org.eclipse.jetty.websocket.api.Session; +import org.eclipse.jetty.websocket.api.WebSocketListener; + +import love.distributedrebirth.no2all.wire.WireServerHandler; + +public class JettyWireServerHandler implements WebSocketListener { + + private final WireServerHandler handler; + private JettyWireServerSocket conn; + + protected JettyWireServerHandler(WireServerHandler handler) { + this.handler = handler; + this.handler.onStart(); + } + + @Override + public void onWebSocketConnect(Session session) { + this.conn = new JettyWireServerSocket(session); + handler.onOpen(conn); + } + + @Override + public void onWebSocketClose(int statusCode, String reason) { + handler.onClose(conn, statusCode, reason, false); + } + + @Override + public void onWebSocketError(Throwable cause) { + handler.onError(conn, cause); + } + + @Override + public void onWebSocketBinary(byte[] payload, int offset, int len) { + handler.onBinary(conn, ByteBuffer.wrap(payload, offset, len)); + } + + @Override + public void onWebSocketText(String message) { + handler.onMessage(conn, message); + } +} diff --git a/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerSocket.java b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerSocket.java new file mode 100644 index 0000000..0e7ee73 --- /dev/null +++ b/no2all-wire-jetty/src/main/java/love/distributedrebirth/no2all/wire/jetty/JettyWireServerSocket.java @@ -0,0 +1,45 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.io.IOException; +import java.nio.ByteBuffer; + +import org.eclipse.jetty.websocket.api.Session; + +import love.distributedrebirth.no2all.wire.WireServerSocket; + +public final class JettyWireServerSocket implements WireServerSocket { + + private final Session session; + + protected JettyWireServerSocket(Session session) { + this.session = session; + } + + @Override + public String getRemoteAddress() { + return session.getRemoteAddress().toString(); + } + + @Override + public void sendMessage(String message) { + try { + session.getRemote().sendString(message); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public void sendBinary(ByteBuffer message) { + try { + session.getRemote().sendBytes(message); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + @Override + public void close(int code, String message) { + session.close(code, message); + } +} diff --git a/no2all-wire-jetty/src/test/java/love/distributedrebirth/no2all/wire/jetty/EventClient.java b/no2all-wire-jetty/src/test/java/love/distributedrebirth/no2all/wire/jetty/EventClient.java new file mode 100644 index 0000000..80d4026 --- /dev/null +++ b/no2all-wire-jetty/src/test/java/love/distributedrebirth/no2all/wire/jetty/EventClient.java @@ -0,0 +1,77 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.ByteBuffer; + +import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class EventClient implements WireClientHandler { + + private static final Logger LOG = LoggerFactory.getLogger(EventClient.class); + + public static void main(String[] args) throws Exception { + EventClient client = new EventClient(); + client.start(); + client.sendHallo(); + client.stop(); + } + + private WebSocketClient client; + private JettyWireClientEndpoint endpoint1; + + public EventClient() throws URISyntaxException { + client = new WebSocketClient(); + endpoint1 = new JettyWireClientEndpoint(new URI("ws://localhost:8080"), client); + } + + public void start() throws Exception { + client.start(); + LOG.info("client startup"); + } + + public void stop() throws Exception { + client.stop(); + } + + public void sendHallo() { + WireClient client1 = endpoint1.wireClient(this); + client1.connect(); + client1.sendMessage("test me"); + } + + @Override + public void onStart() { + LOG.info("client.onStart()"); + } + + @Override + public void onOpen() { + LOG.info("client.onOpen()"); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + LOG.info("client.onClose: "+code); + } + + @Override + public void onError(Throwable error) { + LOG.info("client.onError: "+error.getMessage()); + } + + @Override + public void onMessage(String message) { + LOG.info("client.onMessage: "+message); + } + + @Override + public void onBinary(ByteBuffer message) { + LOG.info("client.onBinary: "+message); + } +} diff --git a/no2all-wire-jetty/src/test/java/love/distributedrebirth/no2all/wire/jetty/EventServer.java b/no2all-wire-jetty/src/test/java/love/distributedrebirth/no2all/wire/jetty/EventServer.java new file mode 100644 index 0000000..fbe8c50 --- /dev/null +++ b/no2all-wire-jetty/src/test/java/love/distributedrebirth/no2all/wire/jetty/EventServer.java @@ -0,0 +1,102 @@ +package love.distributedrebirth.no2all.wire.jetty; + +import java.net.URI; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.component.LifeCycle.Listener; +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.warp.No2AllReactWarpCore; +import love.distributedrebirth.no2all.react.warp.No2AllRtsWarpSpeedMonitor; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServerSocketSendMessage; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocket; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocketOnMessage; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServerLog; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServer; + +public class EventServer { + + private static final Logger LOG = LoggerFactory.getLogger(EventServer.class); + + public static void main(String[] args) throws Exception { + EventServer server = new EventServer(); + server.setPort(8080); + server.start(); + server.join(); + } + + private final Server server; + private final ServerConnector connector; + private JettyWireServerEndpoint endpoint1; + private JettyWireServerEndpoint endpoint2; + private JettyWireServerEndpoint endpoint3; + + public EventServer() { + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + server.setHandler(context); + JettyWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> { + wsContainer.setMaxTextMessageSize(65535); + endpoint1 = new JettyWireServerEndpoint(wsContainer, "/"); + endpoint2 = new JettyWireServerEndpoint(wsContainer, "/event"); + endpoint3 = new JettyWireServerEndpoint(wsContainer, "/other"); + }); + server.addEventListener(new Listener() { + + @Override + public void lifeCycleStarted(LifeCycle event) { + LOG.info("Startup No2AllReactWarpCore"); + No2AllReactWarpCore react = new No2AllReactWarpCore(); + react.load(new No2AllRtsWarpSpeedMonitor()); + react.load(new No2AllRtsServer()); + react.load(new No2AllRtsServerLog()); + + // auto reply + react.registrate(No2AllRtsServer.API, No2AllArtServer.class, artServer -> { + No2AllReactSlot serverSlot = artServer.getData().getSlot(); + react.registrate(serverSlot, No2AllArtServerSocket.class, artSocket -> { + No2AllReactSlot socketSlot = artSocket.getData().getSlot(); + react.registrate(socketSlot, No2AllArtServerSocketOnMessage.class, artMsg -> { + react.fire(socketSlot, new No2AllActServerSocketSendMessage("RE: "+artMsg.getData().getMessage())); + }); + }); + }); + react.fire(No2AllRtsServer.API, new No2AllActServer(endpoint1)); + react.fire(No2AllRtsServer.API, new No2AllActServer(endpoint2)); + react.fire(No2AllRtsServer.API, new No2AllActServer(endpoint3)); + } + }); + } + + public void setPort(int port) { + connector.setPort(port); + } + + public void start() throws Exception { + server.start(); + } + + public URI getURI() { + return server.getURI(); + } + + public void stop() throws Exception { + server.stop(); + } + + public void join() throws InterruptedException { + LOG.info("Use Ctrl+C to stop server"); + server.join(); + } +} diff --git a/no2all-wire-jre/pom.xml b/no2all-wire-jre/pom.xml new file mode 100644 index 0000000..e58a307 --- /dev/null +++ b/no2all-wire-jre/pom.xml @@ -0,0 +1,16 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-wire-jre + No2All-Wire-Jre + + + ${project.groupId} + no2all-wire + + + \ No newline at end of file diff --git a/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClient.java b/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClient.java new file mode 100644 index 0000000..25ffc86 --- /dev/null +++ b/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClient.java @@ -0,0 +1,54 @@ +package love.distributedrebirth.no2all.wire.jre; + +import java.net.URI; +import java.net.http.WebSocket; +import java.nio.ByteBuffer; +import java.util.concurrent.ExecutionException; + +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public final class JreWireClient implements WireClient { + + private final URI uri; + private final WebSocket.Builder wsBuilder; + private final WireClientHandler handler; + private WebSocket webSocket; + + protected JreWireClient(URI uri, WebSocket.Builder wsBuilder, WireClientHandler handler) { + this.uri = uri; + this.wsBuilder = wsBuilder; + this.handler = handler; + } + + @Override + public void connect() { + try { + webSocket = wsBuilder.buildAsync(uri, new JreWireClientHandler(handler)).get(); + } catch (InterruptedException | ExecutionException error) { + handler.onError(error); // handles: Connection refused + } + } + + @Override + public void close(int code, String message) { + if (webSocket != null) { + webSocket.sendClose(code, message); + webSocket = null; + } + } + + @Override + public void sendMessage(String message) { + if (webSocket != null) { + webSocket.sendText(message, true); + } + } + + @Override + public void sendBinary(ByteBuffer message) { + if (webSocket != null) { + webSocket.sendBinary(message, true); + } + } +} diff --git a/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClientEndpoint.java b/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClientEndpoint.java new file mode 100644 index 0000000..b9524a1 --- /dev/null +++ b/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClientEndpoint.java @@ -0,0 +1,29 @@ +package love.distributedrebirth.no2all.wire.jre; + +import java.net.URI; +import java.net.http.WebSocket; + +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientEndpoint; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public final class JreWireClientEndpoint implements WireClientEndpoint { + + private final URI uri; + private final WebSocket.Builder wsBuilder; + + public JreWireClientEndpoint(URI uri, WebSocket.Builder wsBuilder) { + this.wsBuilder = wsBuilder; + this.uri = uri; + } + + @Override + public String wireId() { + return uri.toString(); + } + + @Override + public WireClient wireClient(WireClientHandler handler) { + return new JreWireClient(uri, wsBuilder, handler); + } +} diff --git a/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClientHandler.java b/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClientHandler.java new file mode 100644 index 0000000..a6ebf83 --- /dev/null +++ b/no2all-wire-jre/src/main/java/love/distributedrebirth/no2all/wire/jre/JreWireClientHandler.java @@ -0,0 +1,45 @@ +package love.distributedrebirth.no2all.wire.jre; + +import java.net.http.WebSocket; +import java.nio.ByteBuffer; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public final class JreWireClientHandler implements WebSocket.Listener { + + private final WireClientHandler handler; + + protected JreWireClientHandler(WireClientHandler handler) { + this.handler = handler; + } + + @Override + public void onOpen(WebSocket ws) { + handler.onOpen(); + } + + @Override + public CompletionStage onClose(WebSocket ws, int code, String reason) { + handler.onClose(code, reason, true); + return CompletableFuture.completedFuture(""); + } + + @Override + public void onError(WebSocket ws, Throwable error) { + handler.onError(error); + } + + @Override + public CompletionStage onText(WebSocket ws, CharSequence data, boolean last) { + handler.onMessage(data.toString()); + return CompletableFuture.completedFuture(""); + } + + @Override + public CompletionStage onBinary(WebSocket ws, ByteBuffer data, boolean last) { + handler.onBinary(data); + return CompletableFuture.completedFuture(""); + } +} \ No newline at end of file diff --git a/no2all-wire-ojw/pom.xml b/no2all-wire-ojw/pom.xml new file mode 100644 index 0000000..b53b9db --- /dev/null +++ b/no2all-wire-ojw/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-wire-ojw + No2All-Wire-Org-Java-Websocket + + + ${project.groupId} + no2all-wire + + + org.java-websocket + Java-WebSocket + + + \ No newline at end of file diff --git a/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClient.java b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClient.java new file mode 100644 index 0000000..f76fccb --- /dev/null +++ b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClient.java @@ -0,0 +1,41 @@ +package love.distributedrebirth.no2all.wire.ojw; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Map; + +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class OjwWireClient implements WireClient { + + private final OjwWireClientHandler handler; + + protected OjwWireClient(URI uri, Map headers, int timeoutMs, WireClientHandler handler) { + this.handler = new OjwWireClientHandler(uri, headers, timeoutMs, handler); + } + + public OjwWireClientHandler getHandler() { + return handler; + } + + @Override + public void connect() { + handler.connect(); + } + + @Override + public void close(int code, String message) { + handler.close(code, message); + } + + @Override + public void sendMessage(String message) { + handler.send(message); + } + + @Override + public void sendBinary(ByteBuffer message) { + handler.send(message); + } +} diff --git a/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClientEndpoint.java b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClientEndpoint.java new file mode 100644 index 0000000..d8776a2 --- /dev/null +++ b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClientEndpoint.java @@ -0,0 +1,35 @@ +package love.distributedrebirth.no2all.wire.ojw; + +import java.net.URI; +import java.util.Map; + +import love.distributedrebirth.no2all.wire.WireClient; +import love.distributedrebirth.no2all.wire.WireClientEndpoint; +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class OjwWireClientEndpoint implements WireClientEndpoint { + + private final URI uri; + private final Map headers; + private final int timeoutMs; + + public OjwWireClientEndpoint(URI uri) { + this(uri, null, 60 * 1000); + } + + public OjwWireClientEndpoint(URI uri, Map headers, int timeoutMs) { + this.uri = uri; + this.headers = headers; + this.timeoutMs = timeoutMs; + } + + @Override + public String wireId() { + return uri.toString(); + } + + @Override + public WireClient wireClient(WireClientHandler handler) { + return new OjwWireClient(uri, headers, timeoutMs, handler); + } +} diff --git a/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClientHandler.java b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClientHandler.java new file mode 100644 index 0000000..fdda507 --- /dev/null +++ b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireClientHandler.java @@ -0,0 +1,46 @@ +package love.distributedrebirth.no2all.wire.ojw; + +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.Map; + +import org.java_websocket.client.WebSocketClient; +import org.java_websocket.drafts.Draft_6455; +import org.java_websocket.handshake.ServerHandshake; + +import love.distributedrebirth.no2all.wire.WireClientHandler; + +public class OjwWireClientHandler extends WebSocketClient { + + private final WireClientHandler handler; + + protected OjwWireClientHandler(URI uri, Map headers, int timeoutMs, WireClientHandler handler) { + super(uri, new Draft_6455(), headers, timeoutMs); + this.handler = handler; + } + + @Override + public void onOpen(ServerHandshake handshakedata) { + handler.onOpen(); + } + + @Override + public void onClose(int code, String reason, boolean remote) { + handler.onClose(code, reason, remote); + } + + @Override + public void onError(Exception ex) { + handler.onError(ex); + } + + @Override + public void onMessage(String message) { + handler.onMessage(message); + } + + @Override + public void onMessage(ByteBuffer message) { + handler.onBinary(message); + } +} diff --git a/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServer.java b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServer.java new file mode 100644 index 0000000..ab7283f --- /dev/null +++ b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServer.java @@ -0,0 +1,31 @@ +package love.distributedrebirth.no2all.wire.ojw; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; + +import love.distributedrebirth.no2all.wire.WireServer; +import love.distributedrebirth.no2all.wire.WireServerHandler; + +public class OjwWireServer implements WireServer { + + private final OjwWireServerHandler handler; + + protected OjwWireServer(InetSocketAddress port, int pingTimeoutSecs, WireServerHandler handler) { + this.handler = new OjwWireServerHandler(port, pingTimeoutSecs, handler); + this.handler.start(); + } + + public OjwWireServerHandler getHandler() { + return handler; + } + + @Override + public void broadcastMessage(String message) { + handler.broadcast(message); + } + + @Override + public void broadcastBinary(ByteBuffer message) { + handler.broadcast(message); + } +} diff --git a/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerEndpoint.java b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerEndpoint.java new file mode 100644 index 0000000..be5164c --- /dev/null +++ b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerEndpoint.java @@ -0,0 +1,28 @@ +package love.distributedrebirth.no2all.wire.ojw; + +import java.net.InetSocketAddress; + +import love.distributedrebirth.no2all.wire.WireServer; +import love.distributedrebirth.no2all.wire.WireServerEndpoint; +import love.distributedrebirth.no2all.wire.WireServerHandler; + +public class OjwWireServerEndpoint implements WireServerEndpoint { + + private final InetSocketAddress port; + private final int pingTimeoutSecs; + + public OjwWireServerEndpoint(InetSocketAddress port, int pingTimeoutSecs) { + this.port = port; + this.pingTimeoutSecs = pingTimeoutSecs; + } + + @Override + public String wireId() { + return port.toString(); + } + + @Override + public WireServer wireServer(WireServerHandler handler) { + return new OjwWireServer(port, pingTimeoutSecs, handler); + } +} diff --git a/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerHandler.java b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerHandler.java new file mode 100644 index 0000000..a9815b2 --- /dev/null +++ b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerHandler.java @@ -0,0 +1,81 @@ +package love.distributedrebirth.no2all.wire.ojw; + +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; + +import love.distributedrebirth.no2all.wire.WireServerHandler; + +public class OjwWireServerHandler extends WebSocketServer { + + private final WireServerHandler handler; + private final Map wraps = new WeakHashMap<>(); + private final ReadWriteLock lock = new ReentrantReadWriteLock(); + private final Lock lockRead = lock.readLock(); + private final Lock lockWrite = lock.writeLock(); + + protected OjwWireServerHandler(InetSocketAddress port, int pingTimeoutSecs, WireServerHandler handler) { + super(port); + setConnectionLostTimeout(pingTimeoutSecs); + this.handler = handler; + } + + @Override + public void onStart() { + handler.onStart(); + } + + @Override + public void onOpen(WebSocket conn, ClientHandshake handshake) { + handler.onOpen(wrap(conn)); + } + + @Override + public void onClose(WebSocket conn, int code, String reason, boolean remote) { + handler.onClose(wrap(conn), code, reason, remote); + } + + @Override + public void onError(WebSocket conn, Exception ex) { + handler.onError(wrap(conn), ex); + } + + @Override + public void onMessage(WebSocket conn, String message) { + handler.onMessage(wrap(conn), message); + } + + @Override + public void onMessage(WebSocket conn, ByteBuffer message) { + handler.onBinary(wrap(conn), message); + } + + private OjwWireServerSocket wrap(WebSocket conn) { + OjwWireServerSocket result = null; + lockRead.lock(); + try { + result = wraps.get(conn); + } finally { + lockRead.unlock(); + } + if (result != null) { + return result; + } + result = new OjwWireServerSocket(conn); + lockWrite.lock(); + try { + wraps.put(conn, result); + } finally { + lockWrite.unlock(); + } + return result; + } +} \ No newline at end of file diff --git a/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerSocket.java b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerSocket.java new file mode 100644 index 0000000..787692b --- /dev/null +++ b/no2all-wire-ojw/src/main/java/love/distributedrebirth/no2all/wire/ojw/OjwWireServerSocket.java @@ -0,0 +1,36 @@ +package love.distributedrebirth.no2all.wire.ojw; + +import java.nio.ByteBuffer; + +import org.java_websocket.WebSocket; + +import love.distributedrebirth.no2all.wire.WireServerSocket; + +public final class OjwWireServerSocket implements WireServerSocket { + + private final WebSocket socket; + + protected OjwWireServerSocket(WebSocket socket) { + this.socket = socket; + } + + @Override + public String getRemoteAddress() { + return socket.getRemoteSocketAddress().getAddress().getHostAddress(); + } + + @Override + public void sendMessage(String message) { + socket.send(message); + } + + @Override + public void sendBinary(ByteBuffer message) { + socket.send(message); + } + + @Override + public void close(int code, String message) { + socket.close(code, message); + } +} diff --git a/no2all-wire/pom.xml b/no2all-wire/pom.xml new file mode 100644 index 0000000..07c73c7 --- /dev/null +++ b/no2all-wire/pom.xml @@ -0,0 +1,10 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-wire + No2All-Wire + \ No newline at end of file diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClient.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClient.java new file mode 100644 index 0000000..7136c36 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClient.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.wire; + +import java.nio.ByteBuffer; + +public interface WireClient { + + void connect(); + + void close(int code, String message); + + void sendMessage(String message); + + void sendBinary(ByteBuffer message); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClientEndpoint.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClientEndpoint.java new file mode 100644 index 0000000..c169993 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClientEndpoint.java @@ -0,0 +1,8 @@ +package love.distributedrebirth.no2all.wire; + +public interface WireClientEndpoint { + + String wireId(); + + WireClient wireClient(WireClientHandler handler); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClientHandler.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClientHandler.java new file mode 100644 index 0000000..b34391f --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireClientHandler.java @@ -0,0 +1,18 @@ +package love.distributedrebirth.no2all.wire; + +import java.nio.ByteBuffer; + +public interface WireClientHandler { + + void onStart(); + + void onOpen(); + + void onClose(int code, String reason, boolean remote); + + void onError(Throwable error); + + void onMessage(String message); + + void onBinary(ByteBuffer message); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServer.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServer.java new file mode 100644 index 0000000..5f9b21c --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServer.java @@ -0,0 +1,10 @@ +package love.distributedrebirth.no2all.wire; + +import java.nio.ByteBuffer; + +public interface WireServer { + + void broadcastMessage(String message); + + void broadcastBinary(ByteBuffer message); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerEndpoint.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerEndpoint.java new file mode 100644 index 0000000..985b300 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerEndpoint.java @@ -0,0 +1,8 @@ +package love.distributedrebirth.no2all.wire; + +public interface WireServerEndpoint { + + String wireId(); + + WireServer wireServer(WireServerHandler handler); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerHandler.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerHandler.java new file mode 100644 index 0000000..31556d0 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerHandler.java @@ -0,0 +1,18 @@ +package love.distributedrebirth.no2all.wire; + +import java.nio.ByteBuffer; + +public interface WireServerHandler { + + void onStart(); + + void onOpen(WireServerSocket conn); + + void onClose(WireServerSocket conn, int code, String reason, boolean remote); + + void onError(WireServerSocket conn, Throwable error); + + void onMessage(WireServerSocket conn, String message); + + void onBinary(WireServerSocket conn, ByteBuffer message); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerSocket.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerSocket.java new file mode 100644 index 0000000..bab97b9 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/WireServerSocket.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.wire; + +import java.nio.ByteBuffer; + +public interface WireServerSocket { + + String getRemoteAddress(); + + void sendMessage(String message); + + void sendBinary(ByteBuffer message); + + void close(int code, String message); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetch.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetch.java new file mode 100644 index 0000000..12d0411 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetch.java @@ -0,0 +1,4 @@ +package love.distributedrebirth.no2all.wire.fetch; + +public interface WireFetch extends Runnable { +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchFactory.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchFactory.java new file mode 100644 index 0000000..b231d99 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchFactory.java @@ -0,0 +1,6 @@ +package love.distributedrebirth.no2all.wire.fetch; + +public interface WireFetchFactory { + + WireFetch buildFetcher(WireFetchResource resource, WireFetchHandler handler); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchFactoryDefault.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchFactoryDefault.java new file mode 100644 index 0000000..27814b7 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchFactoryDefault.java @@ -0,0 +1,84 @@ +package love.distributedrebirth.no2all.wire.fetch; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.ByteBuffer; +import java.util.Map; +import java.util.Map.Entry; + +public final class WireFetchFactoryDefault implements WireFetchFactory { + + @Override + public WireFetch buildFetcher(WireFetchResource resource, WireFetchHandler handler) { + return new HttpDownloader(resource, handler); + } + + static protected final class HttpDownloader implements WireFetch { + + private final WireFetchResource resource; + private final WireFetchHandler handler; + + protected HttpDownloader(WireFetchResource resource, WireFetchHandler handler) { + this.resource = resource; + this.handler = handler; + } + + @Override + public void run() { + try { + handler.onStart(); + try (InputStream conn = openInputStream(resource.getUrId().toURL(), resource.getHeaders(), resource.getTimeoutMs(), 5)) { + try (ByteArrayOutputStream result = new ByteArrayOutputStream()) { + int totalBytes = 0; + byte[] buffer = new byte[4096]; + int length; + while ((length = conn.read(buffer)) != -1) { + result.write(buffer, 0, length); + totalBytes += length; + handler.onProgress(totalBytes); + } + handler.onReady(ByteBuffer.wrap(result.toByteArray())); + } + } + } catch (Exception error) { + handler.onError(error); + } + } + + private InputStream openInputStream(URL url, Map args, int timeoutMs, int redirectLimit) throws IOException { + HttpURLConnection con = (HttpURLConnection) url.openConnection(); + con.setConnectTimeout(timeoutMs); + con.setReadTimeout(timeoutMs); + if (args != null) { + for (Entry e : args.entrySet()) { + con.setRequestProperty(e.getKey(), e.getValue()); + } + } + con.connect(); + int responseCode = con.getResponseCode(); + if (responseCode < 400 && responseCode > 299) { + // for redirects but breaks switch protocols + redirectLimit--; + if (redirectLimit == 0) { + throw new IllegalStateException("Max redirect limit reached"); + } + String redirectLocation = con.getHeaderField("Location"); + if (redirectLocation == null) { + throw new IllegalStateException("No redirect location header"); + } + URL redirectUrl = null; + try { + redirectUrl = new URL(redirectLocation); + } catch (MalformedURLException e) { + redirectUrl = new URL(url.getProtocol() + "://" + url.getHost() + redirectLocation); + } + return openInputStream(redirectUrl, args, timeoutMs, redirectLimit); + } + return con.getInputStream(); + } + } +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchHandler.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchHandler.java new file mode 100644 index 0000000..d938306 --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchHandler.java @@ -0,0 +1,14 @@ +package love.distributedrebirth.no2all.wire.fetch; + +import java.nio.ByteBuffer; + +public interface WireFetchHandler { + + void onStart(); + + void onReady(ByteBuffer result); + + void onError(Throwable error); + + void onProgress(int totalBytes); +} diff --git a/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchResource.java b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchResource.java new file mode 100644 index 0000000..520b14b --- /dev/null +++ b/no2all-wire/src/main/java/love/distributedrebirth/no2all/wire/fetch/WireFetchResource.java @@ -0,0 +1,39 @@ +package love.distributedrebirth.no2all.wire.fetch; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +public final class WireFetchResource { + + private final Map headers = new HashMap<>(); + private final URI uri; + private int timeoutMs = 10; + + public WireFetchResource(URI uri) { + this.uri = uri; + this.timeoutMs = 10; + } + + public URI getUrId() { + return uri; + } + + public int getTimeoutMs() { + return timeoutMs; + } + + public Map getHeaders() { + return headers; + } + + public WireFetchResource withTimeoutMs(int timeoutMs) { + this.timeoutMs = timeoutMs; + return this; + } + + public WireFetchResource withHeader(String key, String value) { + headers.put(key, value); + return this; + } +} diff --git a/no2all-zerofungus/conf/logback-server-console.xml b/no2all-zerofungus/conf/logback-server-console.xml new file mode 100644 index 0000000..918cbc0 --- /dev/null +++ b/no2all-zerofungus/conf/logback-server-console.xml @@ -0,0 +1,29 @@ + + + + true + + + + logs/no2all-demo.log + + %date %level [%thread] %logger{10} %msg%n + + + + + %date %level [%thread] %logger{10} %msg%n + + + + + + + + + + + + + + diff --git a/no2all-zerofungus/conf/logback-server.xml b/no2all-zerofungus/conf/logback-server.xml new file mode 100644 index 0000000..933acef --- /dev/null +++ b/no2all-zerofungus/conf/logback-server.xml @@ -0,0 +1,23 @@ + + + + true + + + + logs/no2all-demo.log + + logs/no2all-demo-%d{yyyy-MM-dd}.log.zip + + + %date %level [%thread] %logger{10} %msg%n + + + + + + + + + + diff --git a/no2all-zerofungus/pom.xml b/no2all-zerofungus/pom.xml new file mode 100644 index 0000000..518f989 --- /dev/null +++ b/no2all-zerofungus/pom.xml @@ -0,0 +1,54 @@ + + 4.0.0 + + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + + no2all-zerofungus + No2All-ZeroFungus + + + ${project.groupId} + no2all-react-nostr + + + ${project.groupId} + no2all-wire-jetty + + + ch.qos.logback + logback-classic + + + org.slf4j + jul-to-slf4j + + + org.slf4j + log4j-over-slf4j + + + com.h2database + h2 + + + org.postgresql + postgresql + + + org.flywaydb + flyway-core + + + org.webjars + jquery + 3.5.1 + + + org.webjars + jquery-validation + 1.19.0 + + + \ No newline at end of file diff --git a/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/ZFunServer.java b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/ZFunServer.java new file mode 100644 index 0000000..6327224 --- /dev/null +++ b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/ZFunServer.java @@ -0,0 +1,26 @@ +package love.distributedrebirth.no2all.zerofungus; + +import java.util.logging.Logger; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; + +public class ZFunServer implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(ZFunServer.class); + private static final Logger logger = Logger.getLogger(ZFunServer.class.getName()); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + signal.getReact().claim(API); + + // TODO: reg and refire events + + logger.info("╔═╗┌─┐┬─┐┌─┐ .╔═╗┬ ┬┌┐┌╔═╗┬ ┬┌─┐"); + logger.info("╔═╝├┤ ├┬┘│ │ . ╠╣ │ ││││║ ╦│ │└─┐"); + logger.info("╚═╝└─┘┴└─└─┘. ╚ └─┘┘└┘╚═╝└─┘└─┘"); + logger.info("Boot done."); + } +} diff --git a/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/ZeroFungus.java b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/ZeroFungus.java new file mode 100644 index 0000000..8d05ca6 --- /dev/null +++ b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/ZeroFungus.java @@ -0,0 +1,36 @@ +package love.distributedrebirth.no2all.zerofungus; + +import love.distributedrebirth.no2all.react.warp.No2AllReactWarpCore; +import love.distributedrebirth.no2all.react.warp.No2AllRtsWarpSpeedMonitor; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServerLog; +import love.distributedrebirth.no2all.zerofungus.service.ZFunRtsConfigJetty; +import love.distributedrebirth.no2all.zerofungus.service.ZFunRtsConfigLogger; +//import ᒢᐩᐩ.ᔿᐤᒄʸ.ᣔᒃᣔᒼᓑᔆ.BãßBȍőnAbacusInstanceMBeanʸᴰ; +import love.distributedrebirth.no2all.zerofungus.service.ZFunRtsInitJdbc; + +public enum ZeroFungus /*implements BãßBȍőnAbacusInstanceMBeanʸᴰ*/ { + INSTANCE; + + private No2AllReactWarpCore foei = new No2AllReactWarpCore(); + + public static void main(String[] args) { + INSTANCE.start(); + } + + public void stop() { + //foei.fire(No2AllReactWarpCore.DILITHIUM, new Object()); + } + + public void start() { + foei.load(new ZFunRtsConfigLogger()); + foei.load(new No2AllRtsWarpSpeedMonitor()); + foei.load(new No2AllRtsServer()); + foei.load(new No2AllRtsServerLog()); + foei.load(ZFunRtsConfigJetty.INSTANCE); + foei.load(new ZFunRtsInitJdbc(foei)); + foei.load(new ZFunServer()); + + ZFunRtsConfigJetty.INSTANCE.join(); + } +} diff --git a/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsConfigJetty.java b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsConfigJetty.java new file mode 100644 index 0000000..5da3a2a --- /dev/null +++ b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsConfigJetty.java @@ -0,0 +1,145 @@ +package love.distributedrebirth.no2all.zerofungus.service; + +import java.io.IOException; +import java.net.URL; +import java.util.Collections; +import java.util.List; +import java.util.logging.Logger; + +import org.eclipse.jetty.annotations.AnnotationConfiguration; +import org.eclipse.jetty.server.CustomRequestLog; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.DefaultServlet; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.component.LifeCycle.Listener; +import org.eclipse.jetty.util.resource.Resource; +import org.eclipse.jetty.util.resource.ResourceCollection; +import org.eclipse.jetty.webapp.Configuration; +import org.eclipse.jetty.webapp.WebAppContext; +import org.eclipse.jetty.webapp.WebInfConfiguration; +import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; + +import love.distributedrebirth.no2all.react.No2AllReact; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllActServerSocketSendMessage; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServer; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocket; +import love.distributedrebirth.no2all.react.wire.server.No2AllArtServerSocketOnMessage; +import love.distributedrebirth.no2all.react.wire.server.No2AllRtsServer; +import love.distributedrebirth.no2all.wire.jetty.JettyWireServerEndpoint; +import love.distributedrebirth.no2all.zerofungus.web.RedirectServlet; + +public enum ZFunRtsConfigJetty implements No2AllReactTypeScript { + INSTANCE; + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(ZFunRtsConfigJetty.class); + private static final Logger logger = Logger.getLogger(ZFunRtsConfigJetty.class.getName()); + private final Server server; + private final ServerConnector connector; + private JettyWireServerEndpoint endpoint1; + private JettyWireServerEndpoint endpoint2; + private JettyWireServerEndpoint endpoint3; + + private ZFunRtsConfigJetty() { + server = new Server(); + connector = new ServerConnector(server); + } + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + logger.info(" ╦┌─┐╔╦╗╔╦╗┬ ┬ ╔═╗┌─┐╦ ╦╔═╗┬─┐┌┬┐"); + logger.info(" ║├┤ ║ ║ └┬┘ ╠═╝│ │║║║║╣ ├┬┘ ││"); + logger.info("╚╝└─┘ ╩ ╩ ┴ ╩ └─┘╚╩╝╚═╝┴└──┴┘"); + + connector.setPort(8080); + server.addConnector(connector); + + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + context.setBaseResource(buildBaseResource(getClass().getClassLoader())); + context.setAttribute("org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/target/classes/|.*\\.jar"); + context.setConfigurations(new Configuration[]{ + new AnnotationConfiguration(), + new WebInfConfiguration(), + }); + server.setHandler(context); + + JettyWebSocketServletContainerInitializer.configure(context, (servletContext, wsContainer) -> { + wsContainer.setMaxTextMessageSize(65535); + endpoint1 = new JettyWireServerEndpoint(wsContainer, "/con1"); + endpoint2 = new JettyWireServerEndpoint(wsContainer, "/event"); + endpoint3 = new JettyWireServerEndpoint(wsContainer, "/other"); + }); + + logger.info("Add H2 console on: /debug/jdbc/console"); + context.addServlet("org.h2.server.web.WebServlet", "/debug/jdbc/console/*"); + + context.addServlet(RedirectServlet.class, "/test"); + + // for static + ServletHolder staticHome = new ServletHolder("staticHome", DefaultServlet.class); + staticHome.setInitParameter("dirAllowed","false"); + context.addServlet(staticHome, "/"); + + logger.info("Add request logger console"); + CustomRequestLog log = new CustomRequestLog("logs/http.log", CustomRequestLog.EXTENDED_NCSA_FORMAT); + server.setRequestLog(log); + + No2AllReact react = signal.getReact(); + server.addEventListener(new Listener() { + + @Override + public void lifeCycleStarted(LifeCycle event) { + // auto reply + react.registrate(No2AllRtsServer.API, No2AllArtServer.class, artServer -> { + No2AllReactSlot serverSlot = artServer.getData().getSlot(); + react.registrate(serverSlot, No2AllArtServerSocket.class, artSocket -> { + No2AllReactSlot socketSlot = artSocket.getData().getSlot(); + react.registrate(socketSlot, No2AllArtServerSocketOnMessage.class, artMsg -> { + react.fire(socketSlot, + new No2AllActServerSocketSendMessage("RE: " + artMsg.getData().getMessage())); + }); + }); + }); + react.fire(No2AllRtsServer.API, new No2AllActServer(endpoint1)); + react.fire(No2AllRtsServer.API, new No2AllActServer(endpoint2)); + react.fire(No2AllRtsServer.API, new No2AllActServer(endpoint3)); + } + }); + try { + logger.info("start"); + server.start(); + logger.info("start done"); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + public void join() { + try { + server.join(); + } catch (InterruptedException e) { + throw new IllegalStateException(e); + } + } + + private Resource buildBaseResource(ClassLoader cl) { + try { + List paths = Collections.list(cl.getResources("META-INF/resources")); + Resource[] resources = new Resource[paths.size() + 1]; + for (int i = 0; i < paths.size(); i++) { + resources[i] = Resource.newResource(paths.get(i)); + } + resources[paths.size()] = Resource.newResource("target/classes/static"); + return new ResourceCollection(resources); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsConfigLogger.java b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsConfigLogger.java new file mode 100644 index 0000000..7647ae8 --- /dev/null +++ b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsConfigLogger.java @@ -0,0 +1,52 @@ +package love.distributedrebirth.no2all.zerofungus.service; + +import java.io.File; +import java.net.MalformedURLException; +import java.util.logging.Logger; + +import org.slf4j.LoggerFactory; +import org.slf4j.bridge.SLF4JBridgeHandler; + +import ch.qos.logback.core.status.NopStatusListener; +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; + +public class ZFunRtsConfigLogger implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(ZFunRtsConfigLogger.class); + private static final Logger logger = Logger.getLogger(ZFunRtsConfigLogger.class.getName()); + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + if (System.getProperty("logback.configurationFile") != null) { + File logConfig = null; + if (System.getProperty("java.class.path").contains("classes")) { + logConfig = new File("conf/logback-server-console.xml"); + } else { + logConfig = new File("conf/logback-server.xml"); + } + try { + System.setProperty("logback.configurationFile", logConfig.toURI().toURL().toExternalForm()); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + } + System.setProperty("logback.statusListenerClass",NopStatusListener.class.getName()); + + System.setProperty("logback.configurationFile","conf/logback-server-console.xml"); + org.slf4j.Logger logger2 = LoggerFactory.getLogger("MyTest"); + logger2.debug("Hello world."); + + SLF4JBridgeHandler.removeHandlersForRootLogger(); + SLF4JBridgeHandler.install(); + logger.info("Logging configured"); + logger.info(" ╦┌─┐╔╦╗╔╦╗┬ ┬ ╔═╗┌─┐╦ ╦╔═╗┬─┐┌┬┐"); + logger.info(" ║├┤ ║ ║ └┬┘ ╠═╝│ │║║║║╣ ├┬┘ ││"); + logger.info("╚╝└─┘ ╩ ╩ ┴ ╩ └─┘╚╩╝╚═╝┴└──┴┘"); + + //https://patorjk.com/software/taag + // Calvin S + } +} diff --git a/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsInitJdbc.java b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsInitJdbc.java new file mode 100644 index 0000000..51b0845 --- /dev/null +++ b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/service/ZFunRtsInitJdbc.java @@ -0,0 +1,62 @@ +package love.distributedrebirth.no2all.zerofungus.service; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.logging.Logger; + +import javax.sql.DataSource; + +import org.flywaydb.core.Flyway; +import org.h2.jdbcx.JdbcConnectionPool; + +import love.distributedrebirth.no2all.react.No2AllReactSlot; +import love.distributedrebirth.no2all.react.No2AllReactSlotLoad; +import love.distributedrebirth.no2all.react.No2AllReactSlotSignal; +import love.distributedrebirth.no2all.react.No2AllReactTypeScript; +import love.distributedrebirth.no2all.react.warp.No2AllReactWarpCore; + +public class ZFunRtsInitJdbc implements No2AllReactTypeScript { + + public static final No2AllReactSlot API = No2AllReactSlot.ofClass(ZFunRtsInitJdbc.class); + private static final Logger logger = Logger.getLogger(ZFunRtsInitJdbc.class.getName()); + private final No2AllReactWarpCore foei; + + public ZFunRtsInitJdbc(No2AllReactWarpCore foei) { + this.foei = foei; + } + + @Override + public void onEvent(No2AllReactSlotSignal signal) { + signal.getReact().claim(API); + + JdbcConnectionPool cp = JdbcConnectionPool.create("jdbc:h2:file:./target/h2db/zfun", "sa", "sa"); + foei.addService(JdbcConnectionPool.class, cp); + foei.addService(DataSource.class, cp); + + Flyway flyway = Flyway.configure().schemas("ᔆʸᔆᐪᓫᔿ", "other", "ssd").table("ᣔᒃᣔᒼᓑᔆ").dataSource(cp).load(); + flyway.migrate(); + logger.info("╔╦╗┌─┐╔╦╗┌─┐╔╗ ┌─┐╔═╗┌─┐ ╔═╗┌─┐╔╦╗┬ ┬┌─┐"); + logger.info(" ║║├─┤ ║ ├─┤╠╩╗├─┤╚═╗├┤ ╚═╗├┤ ║ │ │├─┘"); + logger.info("═╩╝┴ ┴ ╩ ┴ ┴╚═╝┴ ┴╚═╝└─┘ ╚═╝└─┘ ╩ └─┘┴ "); + try { + startDone(); + } catch (SQLException e) { + throw new IllegalStateException(e); + } + } + + public void startDone() throws SQLException { + JdbcConnectionPool cp = foei.service(JdbcConnectionPool.class); + try (Connection conn = cp.getConnection()) { + String sql = "SELECT COUNT(\"ᑊᑊᐣ\") FROM \"ᔆʸᔆᐪᓫᔿ\".\"ᔆᐪᣔᒼᒽᑉ\""; + PreparedStatement prep = conn.prepareStatement(sql); + ResultSet rs = prep.executeQuery(); + if (rs.first()) { + Long records = rs.getLong(1); + logger.info("DB Records: " + records + " from: " + sql); + } + } + } +} diff --git a/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/web/RedirectServlet.java b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/web/RedirectServlet.java new file mode 100644 index 0000000..a0d8008 --- /dev/null +++ b/no2all-zerofungus/src/main/java/love/distributedrebirth/no2all/zerofungus/web/RedirectServlet.java @@ -0,0 +1,17 @@ +package love.distributedrebirth.no2all.zerofungus.web; + +import java.io.IOException; + +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class RedirectServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.sendRedirect("/powp/error/errmsg.html?title=NotHere&msg=TheEnd"); + } +} \ No newline at end of file diff --git a/no2all-zerofungus/src/main/resources/db/migration/V00/V00_01__init_db.sql b/no2all-zerofungus/src/main/resources/db/migration/V00/V00_01__init_db.sql new file mode 100644 index 0000000..e8327ec --- /dev/null +++ b/no2all-zerofungus/src/main/resources/db/migration/V00/V00_01__init_db.sql @@ -0,0 +1,37 @@ + +DROP TABLE IF EXISTS "ᒄᒃᓑᔆᓫᣗ"; +CREATE TABLE IF NOT EXISTS "ᒄᒃᓑᔆᓫᣗ" ( + "ᑊᑊᐣ" BIGINT NOT NULL AUTO_INCREMENT, + "ᣕᣔᔿᓫ" VARCHAR(100) NOT NULL, + "ᓑᔆᓫᣗᣕᣔᔿᓫ" VARCHAR(45) NOT NULL, + "ᣖᣔᔆᔆᒡᒢᐤᣗᒄ" VARCHAR(256) NOT NULL, + "ᓫᔿᣔᑊᒻ" VARCHAR(200) NOT NULL, + "ᔆᐪᣔᐪᓑᔆ" VARCHAR(45) NOT NULL, + delete_reason VARCHAR(500) NULL, + create_dt DATETIME NOT NULL DEFAULT now(), + create_uid BIGINT NOT NULL DEFAULT 1, + modify_dt DATE NULL, + modify_uid BIGINT NULL, + PRIMARY KEY ("ᑊᑊᐣ")) + ; + +CREATE UNIQUE INDEX "ᒄᒃᓑᔆᓫᣗᐨᓑᑊᐣᕁᐨᓑᔆᓫᣗᣕᣔᔿᓫ" ON "ᒄᒃᓑᔆᓫᣗ" ("ᓑᔆᓫᣗᣕᣔᔿᓫ" ASC); +CREATE INDEX "ᒄᒃᓑᔆᓫᣗᐨᑊᑊᐣᕁᐨᔆᐪᣔᐪᓑᔆ" ON "ᒄᒃᓑᔆᓫᣗ" ("ᔆᐪᣔᐪᓑᔆ" ASC); + +CREATE TABLE IF NOT EXISTS "ᔆᐪᣔᒼᒽᑉ" ( + "ᑊᑊᐣ" text NOT NULL, + "ᣖᓑᒃᒽᑉᓫᔾ" TEXT NOT NULL, + "ᒼᣗᓫᣔᐪᓫᐝᒄᐪ" DATETIME NOT NULL DEFAULT now(), + "ᑊᑉᑊᣕᒄ" INTEGER NOT NULL, + "ᒄᓫᒻᐪᣔᕐ" TEXT NULL, + "ᓫᕁᣖᑊᣗᓫᐝᒄᐪ" DATETIME NULL, + "ᣗᣔᒡᒢ" JSON NOT NULL, + PRIMARY KEY ("ᑊᑊᐣ")) + ; + +CREATE UNIQUE INDEX IF NOT EXISTS "ᔆᐪᣔᒼᒽᑉᐨᓑᑊᐣᕁᐨᑊᑊᐣ" ON "ᔆᐪᣔᒼᒽᑉ" USING btree ("ᑊᑊᐣ"); +CREATE INDEX IF NOT EXISTS "ᔆᐪᣔᒼᒽᑉᐨᑊᑊᐣᕁᐨᣖᓑᒃᒽᑉᓫᔾ" ON "ᔆᐪᣔᒼᒽᑉ" USING btree ("ᣖᓑᒃᒽᑉᓫᔾ"); +CREATE INDEX IF NOT EXISTS "ᔆᐪᣔᒼᒽᑉᐨᑊᑊᐣᕁᐨᒼᣗᓫᣔᐪᓫᐝᒄᐪ" ON "ᔆᐪᣔᒼᒽᑉ" ("ᒼᣗᓫᣔᐪᓫᐝᒄᐪ" DESC); +CREATE INDEX IF NOT EXISTS "ᔆᐪᣔᒼᒽᑉᐨᑊᑊᐣᕁᐨᑊᑉᑊᣕᒄ" ON "ᔆᐪᣔᒼᒽᑉ" ("ᑊᑉᑊᣕᒄ"); +CREATE INDEX IF NOT EXISTS "ᔆᐪᣔᒼᒽᑉᐨᑊᑊᐣᕁᐨᓫᕁᣖᑊᣗᓫᐝᒄᐪ" ON "ᔆᐪᣔᒼᒽᑉ" ("ᓫᕁᣖᑊᣗᓫᐝᒄᐪ" DESC); + diff --git a/no2all-zerofungus/src/main/resources/db/migration/V00/V00_02__init_data.sql b/no2all-zerofungus/src/main/resources/db/migration/V00/V00_02__init_data.sql new file mode 100644 index 0000000..79763fa --- /dev/null +++ b/no2all-zerofungus/src/main/resources/db/migration/V00/V00_02__init_data.sql @@ -0,0 +1,3 @@ + +INSERT INTO "ᒄᒃᓑᔆᓫᣗ" ("ᑊᑊᐣ", "ᣕᣔᔿᓫ", "ᓑᔆᓫᣗᣕᣔᔿᓫ", "ᣖᣔᔆᔆᒡᒢᐤᣗᒄ", "ᓫᔿᣔᑊᒻ", "ᔆᐪᣔᐪᓑᔆ") VALUES (1, 'Technical User', 'tech', '00000000', 'technical@', 'TECHNICAL'); +INSERT INTO "ᒄᒃᓑᔆᓫᣗ" ("ᑊᑊᐣ", "ᣕᣔᔿᓫ", "ᓑᔆᓫᣗᣕᣔᔿᓫ", "ᣖᣔᔆᔆᒡᒢᐤᣗᒄ", "ᓫᔿᣔᑊᒻ", "ᔆᐪᣔᐪᓑᔆ") VALUES (2, 'Admin', 'admin', '000000000', 'admin@', 'ACTIVE'); diff --git a/no2all-zerofungus/src/main/resources/static/powp/error/errmsg.css b/no2all-zerofungus/src/main/resources/static/powp/error/errmsg.css new file mode 100644 index 0000000..30a34c8 --- /dev/null +++ b/no2all-zerofungus/src/main/resources/static/powp/error/errmsg.css @@ -0,0 +1,35 @@ + +* { + margin: 0; + padding: 0; +} + +body { + font-family: "Helvetica Neue", "Helvetica", "Arial"; + font-size: 14px; + background-color: rgba(45, 67, 49, 0.32); +} + +.container { + width: 500px; + margin: 25px auto; + padding-top: 25px; +} + +.dialog { + padding: 20px; + background-color: #233927; + color: #fff; + -moz-border-radius: 10px; + -webkit-border-radius: 10px; + border-radius: 10px; + margin-top: 25px; +} + +form .dialog, #errorTitle { + color: #ff0000; +} + +.hidden { + display: none; +} diff --git a/no2all-zerofungus/src/main/resources/static/powp/error/errmsg.html b/no2all-zerofungus/src/main/resources/static/powp/error/errmsg.html new file mode 100644 index 0000000..648c57e --- /dev/null +++ b/no2all-zerofungus/src/main/resources/static/powp/error/errmsg.html @@ -0,0 +1,37 @@ + + + + +Error + + + + + + +
+
+

Error

+


+

We could not process your request because;

+

...

+ Home +
+
+ + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..47cd90b --- /dev/null +++ b/pom.xml @@ -0,0 +1,278 @@ + + 4.0.0 + love.distributedrebirth.no2all + no2all + 0.0.1-SNAPSHOT + pom + No2All + + no2all-nostr + no2all-octo + no2all-react + no2all-react-nostr + no2all-react-wire + no2all-wire + no2all-wire-jre + no2all-wire-ojw + no2all-wire-jetty + no2all-zerofungus + + + UTF-8 + 11 + 10.0.15 + 1.2.10 + 2.0.7 + + + + + ${project.groupId} + no2all-nostr + ${project.version} + + + ${project.groupId} + no2all-zap + ${project.version} + + + ${project.groupId} + no2all-octo + ${project.version} + + + ${project.groupId} + no2all-react + ${project.version} + + + ${project.groupId} + no2all-react-nostr + ${project.version} + + + ${project.groupId} + no2all-react-wire + ${project.version} + + + ${project.groupId} + no2all-wire + ${project.version} + + + ${project.groupId} + no2all-wire-jetty + ${project.version} + + + ${project.groupId} + no2all-wire-jre + ${project.version} + + + ${project.groupId} + no2all-wire-ojw + ${project.version} + + + org.eclipse.jetty.websocket + websocket-jetty-api + ${jetty.version} + + + org.eclipse.jetty.websocket + websocket-jetty-server + ${jetty.version} + + + org.eclipse.jetty.websocket + websocket-jetty-client + ${jetty.version} + + + org.eclipse.jetty + jetty-slf4j-impl + ${jetty.version} + + + ch.qos.logback + logback-access + ${logback.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + + + org.bouncycastle + bcprov-jdk18on + 1.73 + + + org.java-websocket + Java-WebSocket + 1.5.3 + + + jakarta.json + jakarta.json-api + 2.1.2 + + + org.eclipse.parsson + parsson + 1.1.0 + + + com.h2database + h2 + 2.1.214 + + + org.postgresql + postgresql + 42.6.0 + + + org.flywaydb + flyway-core + 9.19.1 + + + org.junit.jupiter + junit-jupiter + 5.9.1 + + + + + + github + GitHub Packages + https://maven.pkg.github.com/immetoo/no2all + + + + + org.junit.jupiter + junit-jupiter + test + + + org.eclipse.parsson + parsson + test + + + + ${project.artifactId} + + + + org.apache.maven.plugins + maven-antrun-plugin + 1.8 + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + ${project.build.sourceEncoding} + ${project.build.sourceVersion} + ${project.build.sourceVersion} + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + org.apache.felix + maven-bundle-plugin + 5.1.4 + + + org.apache.maven.plugins + maven-dependency-plugin + 3.3.0 + + + org.apache.maven.plugins + maven-install-plugin + 2.5.2 + + + org.apache.maven.plugins + maven-jar-plugin + 3.3.0 + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-java + + enforce + + + + + [3.6.0,) + + + [11,) + [ERROR] OLD JDK [${java.version}] is used, this project requires JDK 11 or newer + + + + + + + + + + + test-none + + true + + + + build + + + + maven-source-plugin + + + attach-sources + + jar + + + + + + + + + \ No newline at end of file