CreateChannel: оставить единый актуальный формат, убрать legacy v2/v3
This commit is contained in:
parent
e95f65ac78
commit
ddeaf82bfd
@ -1,7 +1,7 @@
|
|||||||
# Типы каналов и CreateChannel v3
|
# Типы каналов и CreateChannel
|
||||||
|
|
||||||
## 1. Формат `CreateChannelBody v3`
|
## 1. Формат `CreateChannelBody`
|
||||||
Формат `TECH_CREATE_CHANNEL` поддерживает `version=3` и включает:
|
Формат `TECH_CREATE_CHANNEL` поддерживает единственный текущий `version=1` и включает:
|
||||||
|
|
||||||
1. line-поля канала (`lineCode`, `prevLineNumber`, `prevLineHash32`, `thisLineNumber`);
|
1. line-поля канала (`lineCode`, `prevLineNumber`, `prevLineHash32`, `thisLineNumber`);
|
||||||
2. `channelName`;
|
2. `channelName`;
|
||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
## 2026-05-13 00:02:32 +0300
|
## 2026-05-13 00:02:32 +0300
|
||||||
- Базовый коммит-ориентир: `f63f40f1eb2f`.
|
- Базовый коммит-ориентир: `f63f40f1eb2f`.
|
||||||
- Добавлен `CreateChannelBody v3` с полями `channelType (2 байта)` и `channelTypeVersion (2 байта)`.
|
- Добавлен текущий формат `CreateChannelBody` с полями `channelType (2 байта)` и `channelTypeVersion (2 байта)`.
|
||||||
- Зафиксированы типы каналов: `0=stories`, `1=public`, `100=personal`, `200=group`.
|
- Зафиксированы типы каналов: `0=stories`, `1=public`, `100=personal`, `200=group`.
|
||||||
- Серверная уникальность имени канала изменена на `owner + type + name(slug)`.
|
- Серверная уникальность имени канала изменена на `owner + type + name(slug)`.
|
||||||
- Root-канал `0` переименован в `stories` на уровне API-чтения.
|
- Root-канал `0` переименован в `stories` на уровне API-чтения.
|
||||||
|
|||||||
@ -4,8 +4,8 @@
|
|||||||
Этот набор файлов — актуальная документация по текущему формату блокчейна SHiNE для каналов, их типов и командных сообщений.
|
Этот набор файлов — актуальная документация по текущему формату блокчейна SHiNE для каналов, их типов и командных сообщений.
|
||||||
|
|
||||||
## Оглавление
|
## Оглавление
|
||||||
1. [01_Channel_Types_and_CreateChannel_v3.md](./01_Channel_Types_and_CreateChannel_v3.md)
|
1. [01_Channel_Types_and_CreateChannel.md](./01_Channel_Types_and_CreateChannel.md)
|
||||||
Формат `CreateChannelBody v3`, типы каналов, уникальность имён и правила `stories`.
|
Текущий формат `CreateChannelBody`, типы каналов, уникальность имён и правила `stories`.
|
||||||
2. [02_Channel_Commands.md](./02_Channel_Commands.md)
|
2. [02_Channel_Commands.md](./02_Channel_Commands.md)
|
||||||
Командные сообщения в каналах: `/.desc`, `/.add`, `/.remove`.
|
Командные сообщения в каналах: `/.desc`, `/.add`, `/.remove`.
|
||||||
3. [CHANGELOG.md](./CHANGELOG.md)
|
3. [CHANGELOG.md](./CHANGELOG.md)
|
||||||
|
|||||||
@ -42,7 +42,7 @@ const MSG_SUBTYPE_REACTION_LIKE = 1;
|
|||||||
const MSG_SUBTYPE_REACTION_UNLIKE = 2;
|
const MSG_SUBTYPE_REACTION_UNLIKE = 2;
|
||||||
const MSG_SUBTYPE_CONNECTION_FOLLOW = 30;
|
const MSG_SUBTYPE_CONNECTION_FOLLOW = 30;
|
||||||
const MSG_SUBTYPE_CONNECTION_UNFOLLOW = 31;
|
const MSG_SUBTYPE_CONNECTION_UNFOLLOW = 31;
|
||||||
const CREATE_CHANNEL_BODY_VERSION = 3;
|
const CREATE_CHANNEL_BODY_VERSION = 1;
|
||||||
const CHANNEL_TYPE_STORIES = 0;
|
const CHANNEL_TYPE_STORIES = 0;
|
||||||
const CHANNEL_TYPE_PUBLIC = 1;
|
const CHANNEL_TYPE_PUBLIC = 1;
|
||||||
const CHANNEL_TYPE_PERSONAL = 100;
|
const CHANNEL_TYPE_PERSONAL = 100;
|
||||||
@ -409,7 +409,7 @@ function normalizeChannelDescription(value) {
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeCreateChannelBodyV3Bytes({
|
function makeCreateChannelBodyBytes({
|
||||||
lineCode,
|
lineCode,
|
||||||
prevLineNumber,
|
prevLineNumber,
|
||||||
prevLineHashHex,
|
prevLineHashHex,
|
||||||
@ -1080,7 +1080,7 @@ export class AuthService {
|
|||||||
msgType: MSG_TYPE_TECH,
|
msgType: MSG_TYPE_TECH,
|
||||||
msgSubType: MSG_SUBTYPE_TECH_CREATE_CHANNEL,
|
msgSubType: MSG_SUBTYPE_TECH_CREATE_CHANNEL,
|
||||||
msgVersion: CREATE_CHANNEL_BODY_VERSION,
|
msgVersion: CREATE_CHANNEL_BODY_VERSION,
|
||||||
bodyBytes: makeCreateChannelBodyV3Bytes({
|
bodyBytes: makeCreateChannelBodyBytes({
|
||||||
lineCode: 0,
|
lineCode: 0,
|
||||||
prevLineNumber,
|
prevLineNumber,
|
||||||
prevLineHashHex,
|
prevLineHashHex,
|
||||||
|
|||||||
@ -16,15 +16,13 @@ public final class BodyRecordParser {
|
|||||||
int v = version & 0xFFFF;
|
int v = version & 0xFFFF;
|
||||||
int st = subType & 0xFFFF;
|
int st = subType & 0xFFFF;
|
||||||
|
|
||||||
// TECH supports Header v1 and CreateChannel v1/v2.
|
// TECH supports Header v1 and CreateChannel current format (ver=1).
|
||||||
if (t == (CreateChannelBody.TYPE & 0xFFFF)) {
|
if (t == (CreateChannelBody.TYPE & 0xFFFF)) {
|
||||||
if (st == (HeaderBody.SUBTYPE_COMPAT & 0xFFFF) && v == (HeaderBody.VER & 0xFFFF)) {
|
if (st == (HeaderBody.SUBTYPE_COMPAT & 0xFFFF) && v == (HeaderBody.VER & 0xFFFF)) {
|
||||||
return new HeaderBody(subType, version, bodyBytes).check();
|
return new HeaderBody(subType, version, bodyBytes).check();
|
||||||
}
|
}
|
||||||
if (st == (CreateChannelBody.SUBTYPE & 0xFFFF)
|
if (st == (CreateChannelBody.SUBTYPE & 0xFFFF)
|
||||||
&& (v == (CreateChannelBody.VER & 0xFFFF)
|
&& (v == (CreateChannelBody.VER & 0xFFFF))) {
|
||||||
|| v == (CreateChannelBody.VER2 & 0xFFFF)
|
|
||||||
|| v == (CreateChannelBody.VER3 & 0xFFFF))) {
|
|
||||||
return new CreateChannelBody(subType, version, bodyBytes).check();
|
return new CreateChannelBody(subType, version, bodyBytes).check();
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
|||||||
@ -11,25 +11,7 @@ import java.util.Objects;
|
|||||||
/**
|
/**
|
||||||
* TECH body for create channel.
|
* TECH body for create channel.
|
||||||
*
|
*
|
||||||
* v1 body bytes:
|
* body bytes:
|
||||||
* [4] lineCode
|
|
||||||
* [4] prevLineNumber
|
|
||||||
* [32] prevLineHash32
|
|
||||||
* [4] thisLineNumber
|
|
||||||
* [1] channelNameLen
|
|
||||||
* [N] channelName UTF-8
|
|
||||||
*
|
|
||||||
* v2 body bytes:
|
|
||||||
* [4] lineCode
|
|
||||||
* [4] prevLineNumber
|
|
||||||
* [32] prevLineHash32
|
|
||||||
* [4] thisLineNumber
|
|
||||||
* [1] channelNameLen
|
|
||||||
* [N] channelName UTF-8
|
|
||||||
* [2] channelDescriptionLen
|
|
||||||
* [M] channelDescription UTF-8 (0..200 bytes)
|
|
||||||
*
|
|
||||||
* v3 body bytes:
|
|
||||||
* [4] lineCode
|
* [4] lineCode
|
||||||
* [4] prevLineNumber
|
* [4] prevLineNumber
|
||||||
* [32] prevLineHash32
|
* [32] prevLineHash32
|
||||||
@ -45,13 +27,7 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
|
|
||||||
public static final short TYPE = 0;
|
public static final short TYPE = 0;
|
||||||
public static final short VER = 1;
|
public static final short VER = 1;
|
||||||
public static final short VER2 = 2;
|
|
||||||
public static final short VER3 = 3;
|
|
||||||
|
|
||||||
public static final int KEY = ((TYPE & 0xFFFF) << 16) | (VER & 0xFFFF);
|
public static final int KEY = ((TYPE & 0xFFFF) << 16) | (VER & 0xFFFF);
|
||||||
public static final int KEY_V2 = ((TYPE & 0xFFFF) << 16) | (VER2 & 0xFFFF);
|
|
||||||
public static final int KEY_V3 = ((TYPE & 0xFFFF) << 16) | (VER3 & 0xFFFF);
|
|
||||||
|
|
||||||
public static final short SUBTYPE = MsgSubType.TECH_CREATE_CHANNEL;
|
public static final short SUBTYPE = MsgSubType.TECH_CREATE_CHANNEL;
|
||||||
|
|
||||||
public static final short CHANNEL_TYPE_STORIES = 0;
|
public static final short CHANNEL_TYPE_STORIES = 0;
|
||||||
@ -79,35 +55,30 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
|
|
||||||
public CreateChannelBody(short subType, short version, byte[] bodyBytes) {
|
public CreateChannelBody(short subType, short version, byte[] bodyBytes) {
|
||||||
Objects.requireNonNull(bodyBytes, "bodyBytes == null");
|
Objects.requireNonNull(bodyBytes, "bodyBytes == null");
|
||||||
|
|
||||||
this.subType = subType;
|
this.subType = subType;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
|
|
||||||
int ver = this.version & 0xFFFF;
|
int ver = this.version & 0xFFFF;
|
||||||
if (ver != (VER & 0xFFFF) && ver != (VER2 & 0xFFFF) && ver != (VER3 & 0xFFFF)) {
|
if (ver != (VER & 0xFFFF)) {
|
||||||
throw new IllegalArgumentException("CreateChannelBody version must be 1, 2 or 3, got=" + ver);
|
throw new IllegalArgumentException("CreateChannelBody version must be 1, got=" + ver);
|
||||||
}
|
}
|
||||||
if ((this.subType & 0xFFFF) != (SUBTYPE & 0xFFFF)) {
|
if ((this.subType & 0xFFFF) != (SUBTYPE & 0xFFFF)) {
|
||||||
throw new IllegalArgumentException("CreateChannelBody subType must be TECH_CREATE_CHANNEL(1), got=" + (this.subType & 0xFFFF));
|
throw new IllegalArgumentException("CreateChannelBody subType must be TECH_CREATE_CHANNEL(1), got=" + (this.subType & 0xFFFF));
|
||||||
}
|
}
|
||||||
|
if (bodyBytes.length < 4 + (4 + 32 + 4) + 1 + 1 + 2 + 4) {
|
||||||
if (bodyBytes.length < 4 + (4 + 32 + 4) + 1 + 1) {
|
|
||||||
throw new IllegalArgumentException("CreateChannelBody too short");
|
throw new IllegalArgumentException("CreateChannelBody too short");
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer bb = ByteBuffer.wrap(bodyBytes).order(ByteOrder.BIG_ENDIAN);
|
ByteBuffer bb = ByteBuffer.wrap(bodyBytes).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
this.lineCode = bb.getInt();
|
this.lineCode = bb.getInt();
|
||||||
this.prevLineNumber = bb.getInt();
|
this.prevLineNumber = bb.getInt();
|
||||||
|
|
||||||
this.prevLineHash32 = new byte[32];
|
this.prevLineHash32 = new byte[32];
|
||||||
bb.get(this.prevLineHash32);
|
bb.get(this.prevLineHash32);
|
||||||
|
|
||||||
this.thisLineNumber = bb.getInt();
|
this.thisLineNumber = bb.getInt();
|
||||||
|
|
||||||
int nameLen = Byte.toUnsignedInt(bb.get());
|
int nameLen = Byte.toUnsignedInt(bb.get());
|
||||||
if (nameLen <= 0) throw new IllegalArgumentException("channelNameLen is 0");
|
if (nameLen <= 0) throw new IllegalArgumentException("channelNameLen is 0");
|
||||||
if (bb.remaining() < nameLen) {
|
if (bb.remaining() < nameLen + 2 + 4) {
|
||||||
throw new IllegalArgumentException("CreateChannelBody tail mismatch: remaining=" + bb.remaining() + " nameLen=" + nameLen);
|
throw new IllegalArgumentException("CreateChannelBody tail mismatch: remaining=" + bb.remaining() + " nameLen=" + nameLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -115,19 +86,13 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
bb.get(nameBytes);
|
bb.get(nameBytes);
|
||||||
this.channelName = new String(nameBytes, StandardCharsets.UTF_8);
|
this.channelName = new String(nameBytes, StandardCharsets.UTF_8);
|
||||||
|
|
||||||
if (ver == (VER2 & 0xFFFF) || ver == (VER3 & 0xFFFF)) {
|
|
||||||
if (bb.remaining() < 2) {
|
|
||||||
throw new IllegalArgumentException("CreateChannelBody v2/v3 missing channelDescriptionLen");
|
|
||||||
}
|
|
||||||
|
|
||||||
int descriptionLen = Short.toUnsignedInt(bb.getShort());
|
int descriptionLen = Short.toUnsignedInt(bb.getShort());
|
||||||
if (descriptionLen > MAX_DESCRIPTION_UTF8_LEN) {
|
if (descriptionLen > MAX_DESCRIPTION_UTF8_LEN) {
|
||||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=200");
|
throw new IllegalArgumentException("channelDescription utf8 len must be <=200");
|
||||||
}
|
}
|
||||||
if (bb.remaining() != descriptionLen) {
|
if (bb.remaining() != descriptionLen + 4) {
|
||||||
throw new IllegalArgumentException("CreateChannelBody v2 tail mismatch: remaining=" + bb.remaining() + " descriptionLen=" + descriptionLen);
|
throw new IllegalArgumentException("CreateChannelBody tail mismatch: remaining=" + bb.remaining() + " descriptionLen=" + descriptionLen);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptionLen == 0) {
|
if (descriptionLen == 0) {
|
||||||
this.channelDescription = "";
|
this.channelDescription = "";
|
||||||
} else {
|
} else {
|
||||||
@ -135,46 +100,11 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
bb.get(descriptionBytes);
|
bb.get(descriptionBytes);
|
||||||
this.channelDescription = normalizeDescription(new String(descriptionBytes, StandardCharsets.UTF_8));
|
this.channelDescription = normalizeDescription(new String(descriptionBytes, StandardCharsets.UTF_8));
|
||||||
}
|
}
|
||||||
if (ver == (VER3 & 0xFFFF)) {
|
|
||||||
if (bb.remaining() < 4) {
|
|
||||||
throw new IllegalArgumentException("CreateChannelBody v3 missing channelTypeCode/channelTypeVersion");
|
|
||||||
}
|
|
||||||
this.channelTypeCode = bb.getShort();
|
this.channelTypeCode = bb.getShort();
|
||||||
this.channelTypeVersion = bb.getShort();
|
this.channelTypeVersion = bb.getShort();
|
||||||
} else {
|
|
||||||
this.channelTypeCode = CHANNEL_TYPE_PUBLIC;
|
|
||||||
this.channelTypeVersion = CHANNEL_TYPE_VERSION_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bb.remaining() != 0) {
|
if (bb.remaining() != 0) {
|
||||||
throw new IllegalArgumentException("Unexpected tail bytes, remaining=" + bb.remaining());
|
throw new IllegalArgumentException("Unexpected tail bytes, remaining=" + bb.remaining());
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.channelDescription = "";
|
|
||||||
this.channelTypeCode = CHANNEL_TYPE_PUBLIC;
|
|
||||||
this.channelTypeVersion = CHANNEL_TYPE_VERSION_DEFAULT;
|
|
||||||
if (bb.remaining() != 0) {
|
|
||||||
throw new IllegalArgumentException("Unexpected tail bytes, remaining=" + bb.remaining());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public CreateChannelBody(int lineCode,
|
|
||||||
int prevLineNumber,
|
|
||||||
byte[] prevLineHash32,
|
|
||||||
int thisLineNumber,
|
|
||||||
String channelName) {
|
|
||||||
this(lineCode, prevLineNumber, prevLineHash32, thisLineNumber, channelName, "", VER);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CreateChannelBody(int lineCode,
|
|
||||||
int prevLineNumber,
|
|
||||||
byte[] prevLineHash32,
|
|
||||||
int thisLineNumber,
|
|
||||||
String channelName,
|
|
||||||
String channelDescription) {
|
|
||||||
this(lineCode, prevLineNumber, prevLineHash32, thisLineNumber, channelName, channelDescription, VER2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CreateChannelBody(int lineCode,
|
public CreateChannelBody(int lineCode,
|
||||||
@ -185,41 +115,15 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
String channelDescription,
|
String channelDescription,
|
||||||
short channelTypeCode,
|
short channelTypeCode,
|
||||||
short channelTypeVersion) {
|
short channelTypeVersion) {
|
||||||
this(lineCode, prevLineNumber, prevLineHash32, thisLineNumber, channelName, channelDescription,
|
|
||||||
channelTypeCode, channelTypeVersion, VER3);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreateChannelBody(int lineCode,
|
|
||||||
int prevLineNumber,
|
|
||||||
byte[] prevLineHash32,
|
|
||||||
int thisLineNumber,
|
|
||||||
String channelName,
|
|
||||||
String channelDescription,
|
|
||||||
short version) {
|
|
||||||
this(lineCode, prevLineNumber, prevLineHash32, thisLineNumber, channelName, channelDescription,
|
|
||||||
CHANNEL_TYPE_PUBLIC, CHANNEL_TYPE_VERSION_DEFAULT, version);
|
|
||||||
}
|
|
||||||
|
|
||||||
private CreateChannelBody(int lineCode,
|
|
||||||
int prevLineNumber,
|
|
||||||
byte[] prevLineHash32,
|
|
||||||
int thisLineNumber,
|
|
||||||
String channelName,
|
|
||||||
String channelDescription,
|
|
||||||
short channelTypeCode,
|
|
||||||
short channelTypeVersion,
|
|
||||||
short version) {
|
|
||||||
Objects.requireNonNull(channelName, "channelName == null");
|
Objects.requireNonNull(channelName, "channelName == null");
|
||||||
if (lineCode < 0) throw new IllegalArgumentException("lineCode < 0");
|
if (lineCode < 0) throw new IllegalArgumentException("lineCode < 0");
|
||||||
|
|
||||||
this.subType = SUBTYPE;
|
this.subType = SUBTYPE;
|
||||||
this.version = version;
|
this.version = VER;
|
||||||
|
|
||||||
this.lineCode = lineCode;
|
this.lineCode = lineCode;
|
||||||
this.prevLineNumber = prevLineNumber;
|
this.prevLineNumber = prevLineNumber;
|
||||||
this.prevLineHash32 = (prevLineHash32 == null ? ZERO32 : Arrays.copyOf(prevLineHash32, 32));
|
this.prevLineHash32 = (prevLineHash32 == null ? ZERO32 : Arrays.copyOf(prevLineHash32, 32));
|
||||||
this.thisLineNumber = thisLineNumber;
|
this.thisLineNumber = thisLineNumber;
|
||||||
|
|
||||||
this.channelName = channelName;
|
this.channelName = channelName;
|
||||||
this.channelDescription = channelDescription == null ? "" : channelDescription;
|
this.channelDescription = channelDescription == null ? "" : channelDescription;
|
||||||
this.channelTypeCode = channelTypeCode;
|
this.channelTypeCode = channelTypeCode;
|
||||||
@ -229,20 +133,14 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
@Override
|
@Override
|
||||||
public CreateChannelBody check() {
|
public CreateChannelBody check() {
|
||||||
if (lineCode < 0) throw new IllegalArgumentException("lineCode < 0");
|
if (lineCode < 0) throw new IllegalArgumentException("lineCode < 0");
|
||||||
|
|
||||||
if ((subType & 0xFFFF) != (SUBTYPE & 0xFFFF)) {
|
if ((subType & 0xFFFF) != (SUBTYPE & 0xFFFF)) {
|
||||||
throw new IllegalArgumentException("CreateChannelBody subType must be TECH_CREATE_CHANNEL(1)");
|
throw new IllegalArgumentException("CreateChannelBody subType must be TECH_CREATE_CHANNEL(1)");
|
||||||
}
|
}
|
||||||
|
|
||||||
String normalizedName = normalizeDisplayName(channelName);
|
String normalizedName = normalizeDisplayName(channelName);
|
||||||
if (normalizedName.isEmpty()) {
|
if (normalizedName.isEmpty()) throw new IllegalArgumentException("channelName is blank");
|
||||||
throw new IllegalArgumentException("channelName is blank");
|
|
||||||
}
|
|
||||||
|
|
||||||
int cpLen = normalizedName.codePointCount(0, normalizedName.length());
|
int cpLen = normalizedName.codePointCount(0, normalizedName.length());
|
||||||
if (cpLen > MAX_NAME_LENGTH) {
|
if (cpLen > MAX_NAME_LENGTH) throw new IllegalArgumentException("channelName length must be <=32");
|
||||||
throw new IllegalArgumentException("channelName length must be <=32");
|
|
||||||
}
|
|
||||||
|
|
||||||
String normalizedDescription = normalizeDescription(channelDescription);
|
String normalizedDescription = normalizeDescription(channelDescription);
|
||||||
byte[] descUtf8 = normalizedDescription.getBytes(StandardCharsets.UTF_8);
|
byte[] descUtf8 = normalizedDescription.getBytes(StandardCharsets.UTF_8);
|
||||||
@ -252,23 +150,12 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
|
|
||||||
int typeCode = Short.toUnsignedInt(channelTypeCode);
|
int typeCode = Short.toUnsignedInt(channelTypeCode);
|
||||||
int typeVer = Short.toUnsignedInt(channelTypeVersion);
|
int typeVer = Short.toUnsignedInt(channelTypeVersion);
|
||||||
if (typeCode < 0 || typeCode > 0xFFFF) {
|
if (typeCode < 0 || typeCode > 0xFFFF) throw new IllegalArgumentException("channelTypeCode invalid");
|
||||||
throw new IllegalArgumentException("channelTypeCode invalid");
|
if (typeVer < 0 || typeVer > 0xFFFF) throw new IllegalArgumentException("channelTypeVersion invalid");
|
||||||
}
|
|
||||||
if (typeVer < 0 || typeVer > 0xFFFF) {
|
|
||||||
throw new IllegalArgumentException("channelTypeVersion invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prevLineNumber < 0) {
|
|
||||||
throw new IllegalArgumentException("prevLineNumber must be >=0 for CreateChannelBody");
|
|
||||||
}
|
|
||||||
if (prevLineHash32 == null || prevLineHash32.length != 32) {
|
|
||||||
throw new IllegalArgumentException("prevLineHash32 invalid");
|
|
||||||
}
|
|
||||||
if (thisLineNumber <= 0) {
|
|
||||||
throw new IllegalArgumentException("thisLineNumber must be >=1 for CreateChannelBody");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (prevLineNumber < 0) throw new IllegalArgumentException("prevLineNumber must be >=0 for CreateChannelBody");
|
||||||
|
if (prevLineHash32 == null || prevLineHash32.length != 32) throw new IllegalArgumentException("prevLineHash32 invalid");
|
||||||
|
if (thisLineNumber <= 0) throw new IllegalArgumentException("thisLineNumber must be >=1 for CreateChannelBody");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,53 +175,33 @@ public final class CreateChannelBody implements BodyRecord, BodyHasLine {
|
|||||||
if (nameUtf8.length == 0 || nameUtf8.length > 255) {
|
if (nameUtf8.length == 0 || nameUtf8.length > 255) {
|
||||||
throw new IllegalArgumentException("channelName utf8 len must be 1..255");
|
throw new IllegalArgumentException("channelName utf8 len must be 1..255");
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isV2 = (version & 0xFFFF) == (VER2 & 0xFFFF);
|
|
||||||
boolean isV3 = (version & 0xFFFF) == (VER3 & 0xFFFF);
|
|
||||||
byte[] descriptionUtf8 = normalizeDescription(channelDescription).getBytes(StandardCharsets.UTF_8);
|
byte[] descriptionUtf8 = normalizeDescription(channelDescription).getBytes(StandardCharsets.UTF_8);
|
||||||
if (descriptionUtf8.length > MAX_DESCRIPTION_UTF8_LEN) {
|
if (descriptionUtf8.length > MAX_DESCRIPTION_UTF8_LEN) {
|
||||||
throw new IllegalArgumentException("channelDescription utf8 len must be <=200");
|
throw new IllegalArgumentException("channelDescription utf8 len must be <=200");
|
||||||
}
|
}
|
||||||
|
|
||||||
int cap = 4 + (4 + 32 + 4) + 1 + nameUtf8.length
|
int cap = 4 + (4 + 32 + 4) + 1 + nameUtf8.length + 2 + descriptionUtf8.length + 4;
|
||||||
+ ((isV2 || isV3) ? 2 + descriptionUtf8.length : 0)
|
|
||||||
+ (isV3 ? 4 : 0);
|
|
||||||
ByteBuffer bb = ByteBuffer.allocate(cap).order(ByteOrder.BIG_ENDIAN);
|
ByteBuffer bb = ByteBuffer.allocate(cap).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
|
||||||
bb.putInt(lineCode);
|
bb.putInt(lineCode);
|
||||||
bb.putInt(prevLineNumber);
|
bb.putInt(prevLineNumber);
|
||||||
bb.put(prevLineHash32 == null ? ZERO32 : Arrays.copyOf(prevLineHash32, 32));
|
bb.put(prevLineHash32 == null ? ZERO32 : Arrays.copyOf(prevLineHash32, 32));
|
||||||
bb.putInt(thisLineNumber);
|
bb.putInt(thisLineNumber);
|
||||||
|
|
||||||
bb.put((byte) nameUtf8.length);
|
bb.put((byte) nameUtf8.length);
|
||||||
bb.put(nameUtf8);
|
bb.put(nameUtf8);
|
||||||
|
|
||||||
if (isV2 || isV3) {
|
|
||||||
bb.putShort((short) (descriptionUtf8.length & 0xFFFF));
|
bb.putShort((short) (descriptionUtf8.length & 0xFFFF));
|
||||||
if (descriptionUtf8.length > 0) {
|
if (descriptionUtf8.length > 0) bb.put(descriptionUtf8);
|
||||||
bb.put(descriptionUtf8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isV3) {
|
|
||||||
bb.putShort(channelTypeCode);
|
bb.putShort(channelTypeCode);
|
||||||
bb.putShort(channelTypeVersion);
|
bb.putShort(channelTypeVersion);
|
||||||
}
|
|
||||||
|
|
||||||
return bb.array();
|
return bb.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int lineCode() { return lineCode; }
|
public int lineCode() { return lineCode; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int prevLineBlockGlobalNumber() { return prevLineNumber; }
|
public int prevLineBlockGlobalNumber() { return prevLineNumber; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] prevLineBlockHash32() {
|
public byte[] prevLineBlockHash32() { return prevLineHash32 == null ? null : Arrays.copyOf(prevLineHash32, 32); }
|
||||||
return prevLineHash32 == null ? null : Arrays.copyOf(prevLineHash32, 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int lineSeq() { return thisLineNumber; }
|
public int lineSeq() { return thisLineNumber; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -105,7 +105,10 @@ public class IT_03_AddBlock_NoAuth {
|
|||||||
sender1.send(new CreateChannelBody(
|
sender1.send(new CreateChannelBody(
|
||||||
0, // lineCode TECH
|
0, // lineCode TECH
|
||||||
ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
|
ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
|
||||||
"News"
|
"News",
|
||||||
|
"",
|
||||||
|
CreateChannelBody.CHANNEL_TYPE_PUBLIC,
|
||||||
|
CreateChannelBody.CHANNEL_TYPE_VERSION_DEFAULT
|
||||||
), t);
|
), t);
|
||||||
|
|
||||||
newsRootBlock = st1.lastBlockNumber(); // root канала = blockNumber этого CREATE_CHANNEL
|
newsRootBlock = st1.lastBlockNumber(); // root канала = blockNumber этого CREATE_CHANNEL
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user