Files
SHiNE-server/SHiNE-server/shine-server-blockchain/src/main/java/blockchain/body/StatusActionBody.java
T
AidarKC 3281da7f5c Очень сильно переделать формат блоков
Внимание: версия ещё не проверена.
2026-10-01 09:34:19 +03:00

88 lines
4.9 KiB
Java

package blockchain.body;
import blockchain.MsgSubType;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;
/** STATUS_ACTION. External target = login + forkNumber + blockNumber + hash32. */
public final class StatusActionBody implements BodyRecord, BodyHasTarget {
public static final short TYPE = 5;
public static final short VER = 1;
public static final int KEY = ((TYPE & 0xFFFF) << 16) | (VER & 0xFFFF);
public final short subType;
public final short version;
public final String toLogin;
public final int toForkNumber;
public final int toBlockGlobalNumber;
public final byte[] toBlockHash32;
public final String message;
public StatusActionBody(short subType, short version, byte[] bodyBytes) {
Objects.requireNonNull(bodyBytes, "bodyBytes == null");
this.subType = subType; this.version = version;
if ((version & 0xFFFF) != 1 || !isSupportedSubType(subType)) throw new IllegalArgumentException("Bad STATUS_ACTION header");
ByteBuffer bb = ByteBuffer.wrap(bodyBytes).order(ByteOrder.BIG_ENDIAN);
BodyTargetSupport.ExternalTarget t = BodyTargetSupport.readExternal(bb, "STATUS_ACTION", 2);
this.toLogin = t.login(); this.toForkNumber = t.forkNumber(); this.toBlockGlobalNumber = t.blockNumber(); this.toBlockHash32 = t.blockHash32();
this.message = readText(bb);
if (bb.hasRemaining()) throw new IllegalArgumentException("Unexpected STATUS_ACTION tail");
}
public StatusActionBody(short subType, String toLogin, int toForkNumber, int toBlockGlobalNumber, byte[] toBlockHash32, String message) {
Objects.requireNonNull(message, "message == null");
if (!isSupportedSubType(subType)) throw new IllegalArgumentException("Unsupported STATUS_ACTION subType");
BodyTargetSupport.validateExternal(toLogin, toForkNumber, toBlockGlobalNumber, toBlockHash32, "STATUS_ACTION");
this.subType = subType; this.version = VER; this.toLogin = toLogin; this.toForkNumber = toForkNumber;
this.toBlockGlobalNumber = toBlockGlobalNumber; this.toBlockHash32 = Arrays.copyOf(toBlockHash32, 32); this.message = message;
}
@Override public StatusActionBody check() {
if (!isSupportedSubType(subType)) throw new IllegalArgumentException("Bad STATUS_ACTION subType");
BodyTargetSupport.validateExternal(toLogin, toForkNumber, toBlockGlobalNumber, toBlockHash32, "STATUS_ACTION");
if (message == null) throw new IllegalArgumentException("message is null");
if (message.getBytes(StandardCharsets.UTF_8).length > 65535) throw new IllegalArgumentException("Text too long");
return this;
}
@Override public byte[] toBytes() {
check();
byte[] text = message.getBytes(StandardCharsets.UTF_8);
ByteBuffer bb = ByteBuffer.allocate(BodyTargetSupport.externalSize(toLogin) + 2 + text.length).order(ByteOrder.BIG_ENDIAN);
BodyTargetSupport.writeExternal(bb, toLogin, toForkNumber, toBlockGlobalNumber, toBlockHash32, "STATUS_ACTION");
bb.putShort((short) text.length).put(text);
return bb.array();
}
@Override public String toLogin() { return toLogin; }
@Override public Integer toForkNumber() { return toForkNumber; }
@Override public Integer toBlockGlobalNumber() { return toBlockGlobalNumber; }
@Override public byte[] toBlockHashBytes() { return Arrays.copyOf(toBlockHash32, 32); }
private static boolean isSupportedSubType(short subType) {
int st = subType & 0xFFFF;
return st == (MsgSubType.STATUS_DONE_ONCE & 0xFFFF) || st == (MsgSubType.STATUS_LEARNED & 0xFFFF)
|| st == (MsgSubType.STATUS_SERVICE_PASSED & 0xFFFF) || st == (MsgSubType.STATUS_CONFIRMED & 0xFFFF)
|| st == (MsgSubType.STATUS_INTERESTED & 0xFFFF) || st == (MsgSubType.STATUS_STARTED & 0xFFFF)
|| st == (MsgSubType.STATUS_IN_STUDY & 0xFFFF) || st == (MsgSubType.STATUS_ABANDONED & 0xFFFF)
|| st == (MsgSubType.STATUS_COMPLETED & 0xFFFF);
}
private static String readText(ByteBuffer bb) {
if (bb.remaining() < 2) throw new IllegalArgumentException("STATUS_ACTION text length missing");
int len = Short.toUnsignedInt(bb.getShort());
if (bb.remaining() < len) throw new IllegalArgumentException("STATUS_ACTION text payload too short");
if (len == 0) return "";
byte[] bytes = new byte[len]; bb.get(bytes);
var dec = StandardCharsets.UTF_8.newDecoder().onMalformedInput(CodingErrorAction.REPORT).onUnmappableCharacter(CodingErrorAction.REPORT);
try { return dec.decode(ByteBuffer.wrap(bytes)).toString(); }
catch (CharacterCodingException e) { throw new IllegalArgumentException("STATUS_ACTION text invalid UTF-8", e); }
}
}