SHA256
Каналы: создавать профиль одним блоком
This commit is contained in:
+6
-7
@@ -19,7 +19,7 @@ import java.util.Objects;
|
||||
* [1] channelNameLen
|
||||
* [N] channelName UTF-8
|
||||
* [2] channelDescriptionLen
|
||||
* [M] channelDescription UTF-8 (0..200 bytes)
|
||||
* [M] channelDescription UTF-8 (0..2048 bytes)
|
||||
* [2] channelTypeCode (uint16)
|
||||
* [2] channelTypeVersion (uint16)
|
||||
*/
|
||||
@@ -38,7 +38,7 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
||||
|
||||
private static final byte[] ZERO32 = new byte[32];
|
||||
private static final int MAX_NAME_LENGTH = 32;
|
||||
private static final int MAX_DESCRIPTION_UTF8_LEN = 200;
|
||||
public static final int MAX_DESCRIPTION_UTF8_LEN = 2048;
|
||||
|
||||
public final short subType;
|
||||
public final short version;
|
||||
@@ -88,7 +88,7 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
||||
|
||||
int descriptionLen = Short.toUnsignedInt(bb.getShort());
|
||||
if (descriptionLen > MAX_DESCRIPTION_UTF8_LEN) {
|
||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=200");
|
||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=2048");
|
||||
}
|
||||
if (bb.remaining() != descriptionLen + 4) {
|
||||
throw new IllegalArgumentException("CreateChannelBody tail mismatch: remaining=" + bb.remaining() + " descriptionLen=" + descriptionLen);
|
||||
@@ -145,7 +145,7 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
||||
String normalizedDescription = normalizeDescription(channelDescription);
|
||||
byte[] descUtf8 = normalizedDescription.getBytes(StandardCharsets.UTF_8);
|
||||
if (descUtf8.length > MAX_DESCRIPTION_UTF8_LEN) {
|
||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=200");
|
||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=2048");
|
||||
}
|
||||
|
||||
int typeCode = Short.toUnsignedInt(channelTypeCode);
|
||||
@@ -166,7 +166,7 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
||||
|
||||
private static String normalizeDescription(String value) {
|
||||
if (value == null) return "";
|
||||
return value.trim().replaceAll("\\s+", " ");
|
||||
return value.trim().replace("\r\n", "\n").replace('\r', '\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -177,7 +177,7 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
||||
}
|
||||
byte[] descriptionUtf8 = normalizeDescription(channelDescription).getBytes(StandardCharsets.UTF_8);
|
||||
if (descriptionUtf8.length > MAX_DESCRIPTION_UTF8_LEN) {
|
||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=200");
|
||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=2048");
|
||||
}
|
||||
|
||||
int cap = 4 + (4 + 32 + 4) + 1 + nameUtf8.length + 2 + descriptionUtf8.length + 4;
|
||||
@@ -204,4 +204,3 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
||||
@Override
|
||||
public int lineSeq() { return thisLineNumber; }
|
||||
}
|
||||
|
||||
|
||||
+28
-6
@@ -304,14 +304,36 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
return new AddBlockResult(WireCodes.Status.INTERNAL_ERROR, "internal_error", serverLastNum, serverLastHashHex);
|
||||
}
|
||||
|
||||
String displayName = normalizedName;
|
||||
String channelDescription = createChannelBody.channelDescription == null
|
||||
? ""
|
||||
: ChannelNameRules.normalizeDisplayName(createChannelBody.channelDescription);
|
||||
String avaAr = "";
|
||||
String avaSha256 = "";
|
||||
long avaSize = 0L;
|
||||
long metaUpdatedAtMs = 0L;
|
||||
if (channelTypeCode == (CreateChannelBody.CHANNEL_TYPE_PUBLIC & 0xFFFF)) {
|
||||
try {
|
||||
ChannelMetaTextParser.Parsed createMeta = ChannelMetaTextParser.parseStrict(channelDescription, normalizedName);
|
||||
displayName = createMeta.title();
|
||||
channelDescription = createMeta.description();
|
||||
avaAr = createMeta.avaAr();
|
||||
avaSha256 = createMeta.avaSha256();
|
||||
avaSize = createMeta.avaSize();
|
||||
metaUpdatedAtMs = block.timestamp * 1000L;
|
||||
} catch (IllegalArgumentException e) {
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, e.getMessage(), serverLastNum, serverLastHashHex);
|
||||
}
|
||||
}
|
||||
|
||||
channelNameStateEntry = new ChannelNameStateEntry();
|
||||
channelNameStateEntry.setSlug(slug);
|
||||
channelNameStateEntry.setDisplayName(normalizedName);
|
||||
channelNameStateEntry.setChannelDescription(
|
||||
createChannelBody.channelDescription == null
|
||||
? ""
|
||||
: ChannelNameRules.normalizeDisplayName(createChannelBody.channelDescription)
|
||||
);
|
||||
channelNameStateEntry.setDisplayName(displayName);
|
||||
channelNameStateEntry.setChannelDescription(channelDescription);
|
||||
channelNameStateEntry.setAvaAr(avaAr);
|
||||
channelNameStateEntry.setAvaSha256(avaSha256);
|
||||
channelNameStateEntry.setAvaSize(avaSize);
|
||||
channelNameStateEntry.setMetaUpdatedAtMs(metaUpdatedAtMs);
|
||||
channelNameStateEntry.setOwnerLogin(login);
|
||||
channelNameStateEntry.setOwnerBlockchainName(blockchainName);
|
||||
channelNameStateEntry.setChannelTypeCode(channelTypeCode);
|
||||
|
||||
+73
-2
@@ -1,7 +1,9 @@
|
||||
package server.logic.ws_protocol.JSON.handlers.channels;
|
||||
|
||||
import blockchain.BchBlockEntry;
|
||||
import blockchain.MsgSubType;
|
||||
import blockchain.body.CreateChannelBody;
|
||||
import blockchain.body.TextLineBody;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import shine.db.DbController;
|
||||
@@ -20,6 +22,7 @@ import java.util.Map;
|
||||
public final class ChannelNamesStateBootstrapper {
|
||||
private static final Logger log = LoggerFactory.getLogger(ChannelNamesStateBootstrapper.class);
|
||||
private static final int MSG_TYPE_TECH = 0;
|
||||
private static final int MSG_TYPE_TEXT = 1;
|
||||
private static final int MSG_SUB_TYPE_CREATE_CHANNEL = 1;
|
||||
private static volatile boolean bootstrapped;
|
||||
|
||||
@@ -74,17 +77,30 @@ public final class ChannelNamesStateBootstrapper {
|
||||
continue;
|
||||
}
|
||||
|
||||
final String displayName;
|
||||
String displayName;
|
||||
final String slug;
|
||||
final String channelDescription;
|
||||
String channelDescription;
|
||||
final int channelTypeCode;
|
||||
final int channelTypeVersion;
|
||||
String avaAr = "";
|
||||
String avaSha256 = "";
|
||||
long avaSize = 0L;
|
||||
long metaUpdatedAtMs = 0L;
|
||||
try {
|
||||
displayName = ChannelNameRules.normalizeDisplayName(createChannelBody.channelName);
|
||||
slug = ChannelNameRules.toCanonicalSlug(displayName);
|
||||
channelDescription = ChannelNameRules.normalizeDisplayName(createChannelBody.channelDescription);
|
||||
channelTypeCode = Short.toUnsignedInt(createChannelBody.channelTypeCode);
|
||||
channelTypeVersion = Short.toUnsignedInt(createChannelBody.channelTypeVersion);
|
||||
if (channelTypeCode == (CreateChannelBody.CHANNEL_TYPE_PUBLIC & 0xFFFF)) {
|
||||
ChannelMetaTextParser.Parsed createMeta = ChannelMetaTextParser.parseStrict(channelDescription, displayName);
|
||||
displayName = createMeta.title();
|
||||
channelDescription = createMeta.description();
|
||||
avaAr = createMeta.avaAr();
|
||||
avaSha256 = createMeta.avaSha256();
|
||||
avaSize = createMeta.avaSize();
|
||||
metaUpdatedAtMs = parsed.timestamp * 1000L;
|
||||
}
|
||||
} catch (Exception badName) {
|
||||
skipped.add(ownerBch + "#" + blockNumber + " (invalid_name)");
|
||||
continue;
|
||||
@@ -102,6 +118,10 @@ public final class ChannelNamesStateBootstrapper {
|
||||
entry.setSlug(slug);
|
||||
entry.setDisplayName(displayName);
|
||||
entry.setChannelDescription(channelDescription == null ? "" : channelDescription);
|
||||
entry.setAvaAr(avaAr);
|
||||
entry.setAvaSha256(avaSha256);
|
||||
entry.setAvaSize(avaSize);
|
||||
entry.setMetaUpdatedAtMs(metaUpdatedAtMs);
|
||||
entry.setOwnerLogin(ownerLogin);
|
||||
entry.setOwnerBlockchainName(ownerBch);
|
||||
entry.setChannelTypeCode(channelTypeCode);
|
||||
@@ -116,6 +136,8 @@ public final class ChannelNamesStateBootstrapper {
|
||||
|
||||
dao.clearAll(c);
|
||||
dao.insertAll(c, entries);
|
||||
|
||||
applyMetaUpdates(c, dao, entries, skipped);
|
||||
c.commit();
|
||||
log.info("channel_names_state bootstrapped: {}", entries.size());
|
||||
if (!conflicts.isEmpty()) {
|
||||
@@ -148,4 +170,53 @@ public final class ChannelNamesStateBootstrapper {
|
||||
throw new RuntimeException("Failed to bootstrap channel_names_state", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyMetaUpdates(Connection c,
|
||||
ChannelNameStateDAO dao,
|
||||
List<ChannelNameStateEntry> entries,
|
||||
List<String> skipped) throws Exception {
|
||||
Map<String, String> fallbackTitles = new LinkedHashMap<>();
|
||||
for (ChannelNameStateEntry entry : entries) {
|
||||
fallbackTitles.put(entry.getOwnerBlockchainName() + "#" + entry.getChannelRootBlockNumber(), entry.getSlug());
|
||||
}
|
||||
|
||||
String sql = """
|
||||
SELECT bch_name, block_number, block_bytes
|
||||
FROM blocks
|
||||
WHERE msg_type = ? AND msg_sub_type = ?
|
||||
ORDER BY bch_name, block_number
|
||||
""";
|
||||
try (PreparedStatement ps = c.prepareStatement(sql)) {
|
||||
ps.setInt(1, MSG_TYPE_TEXT);
|
||||
ps.setInt(2, MsgSubType.TEXT_CHANNEL_META);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
String ownerBch = rs.getString("bch_name");
|
||||
int blockNumber = rs.getInt("block_number");
|
||||
byte[] blockBytes = rs.getBytes("block_bytes");
|
||||
try {
|
||||
BchBlockEntry parsed = new BchBlockEntry(blockBytes);
|
||||
if (!(parsed.body instanceof TextLineBody metaBody)) continue;
|
||||
int lineCode = metaBody.lineCode();
|
||||
String fallbackTitle = fallbackTitles.get(ownerBch + "#" + lineCode);
|
||||
if (fallbackTitle == null) continue;
|
||||
ChannelMetaTextParser.Parsed parsedMeta = ChannelMetaTextParser.parseStrict(metaBody.message, fallbackTitle);
|
||||
|
||||
ChannelNameStateEntry update = new ChannelNameStateEntry();
|
||||
update.setOwnerBlockchainName(ownerBch);
|
||||
update.setChannelRootBlockNumber(lineCode);
|
||||
update.setDisplayName(parsedMeta.title());
|
||||
update.setChannelDescription(parsedMeta.description());
|
||||
update.setAvaAr(parsedMeta.avaAr());
|
||||
update.setAvaSha256(parsedMeta.avaSha256());
|
||||
update.setAvaSize(parsedMeta.avaSize());
|
||||
update.setMetaUpdatedAtMs(parsed.timestamp * 1000L);
|
||||
dao.updateMeta(c, update);
|
||||
} catch (Exception e) {
|
||||
skipped.add(ownerBch + "#" + blockNumber + " (invalid_meta)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-3
@@ -99,6 +99,15 @@ final class ChannelsReadSupport {
|
||||
meta.channelDescription = ccb.channelDescription == null ? "" : ccb.channelDescription;
|
||||
meta.channelTypeCode = Short.toUnsignedInt(ccb.channelTypeCode);
|
||||
meta.channelTypeVersion = Short.toUnsignedInt(ccb.channelTypeVersion);
|
||||
if (meta.channelTypeCode == (CreateChannelBody.CHANNEL_TYPE_PUBLIC & 0xFFFF)) {
|
||||
ChannelMetaTextParser.Parsed parsedMeta = ChannelMetaTextParser.parseStrict(meta.channelDescription, meta.channelName);
|
||||
meta.displayName = parsedMeta.title();
|
||||
meta.channelDescription = parsedMeta.description();
|
||||
meta.avaAr = parsedMeta.avaAr();
|
||||
meta.avaSha256 = parsedMeta.avaSha256();
|
||||
meta.avaSize = parsedMeta.avaSize();
|
||||
meta.metaUpdatedAtMs = e.timestamp * 1000L;
|
||||
}
|
||||
} else {
|
||||
meta.channelName = null;
|
||||
meta.displayName = null;
|
||||
@@ -415,6 +424,14 @@ final class ChannelsReadSupport {
|
||||
if (entry.body instanceof CreateChannelBody ccb) {
|
||||
event.title = ccb.channelName;
|
||||
event.description = ccb.channelDescription == null ? "" : ccb.channelDescription;
|
||||
if (Short.toUnsignedInt(ccb.channelTypeCode) == (CreateChannelBody.CHANNEL_TYPE_PUBLIC & 0xFFFF)) {
|
||||
ChannelMetaTextParser.Parsed parsedMeta = ChannelMetaTextParser.parseStrict(event.description, ccb.channelName);
|
||||
event.title = parsedMeta.title();
|
||||
event.description = parsedMeta.description();
|
||||
event.avaAr = parsedMeta.avaAr();
|
||||
event.avaSha256 = parsedMeta.avaSha256();
|
||||
event.avaSize = parsedMeta.avaSize();
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
event.createdAtMs = 0L;
|
||||
@@ -423,9 +440,8 @@ final class ChannelsReadSupport {
|
||||
event.title = base.displayName == null || base.displayName.isBlank() ? base.channelName : base.displayName;
|
||||
}
|
||||
if (event.description == null) event.description = "";
|
||||
event.avaAr = "";
|
||||
event.avaSha256 = "";
|
||||
event.avaSize = 0L;
|
||||
if (event.avaAr == null) event.avaAr = "";
|
||||
if (event.avaSha256 == null) event.avaSha256 = "";
|
||||
out.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user